Разбор файлов более 2,15 ГБ в Java с использованием Kaitai Struct - PullRequest
9 голосов
/ 20 мая 2019

Я анализирую большие файлы PCAP на Java с помощью Kaitai-Struct.Всякий раз, когда размер файла превышает Integer.MAX_VALUE байт, я сталкиваюсь с IllegalArgumentException, вызванным ограничением размера базового ByteBuffer.

.это не ограничение библиотеки, а ошибка в том, как я ее использую.

Поскольку проблема вызвана попыткой отобразить весь файл в ByteBuffer, я думаю, что решение будетотображая только первую область файла, и по мере того, как данные используются, карта снова пропускает уже проанализированные данные.

Поскольку это делается в библиотеке времени выполнения Kaitai Struct, это будет означать написание моего собственного класса, расширяющего fomKatiaiStream и перезаписать автоматически сгенерированный метод fromFile(...), и это на самом деле не выглядит правильным подходом.

Автоматически сгенерированный метод для анализа файла для класса PCAP:

public static Pcap fromFile(String fileName) throws IOException {
  return new Pcap(new ByteBufferKaitaiStream(fileName));
}

А ByteBufferKaitaiStream, предоставляемый библиотекой времени выполнения Kaitai Struct, поддерживается ByteBuffer.

private final FileChannel fc;
private final ByteBuffer bb;

public ByteBufferKaitaiStream(String fileName) throws IOException {
    fc = FileChannel.open(Paths.get(fileName), StandardOpenOption.READ);
    bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
}

, который, в свою очередь, ограничен максимальным размером ByteBuffer.

Мне не хватает очевидного обходного пути?Действительно ли это ограничение реализации Katiati Struct в Java?

Ответы [ 2 ]

1 голос
/ 20 мая 2019

Здесь есть две отдельные проблемы:

  1. Запуск Pcap.fromFile() для больших файлов, как правило, не очень эффективный метод, так как в конечном итоге вы получите все файлы разбираются в массиве памяти сразу.Пример того, как этого избежать, приведен в kaitai_struct / Issues / 255 .Основная идея состоит в том, что вы захотите контролировать, как вы читаете каждый пакет, а затем утилизируете каждый пакет после того, как вы его как-то проанализировали / учли.

  2. 2 ГБ ограничение наMmaped файлы Java.Чтобы смягчить это, вы можете использовать альтернативную реализацию KaitaiStream на основе RandomAccessFile: RandomAccessFileKaitaiStream - это может быть медленнее, но следует избегать этой проблемы 2 ГБ.

1 голос
/ 20 мая 2019

Эта библиотека предоставляет реализацию ByteBuffer, которая использует смещение long.Я не пробовал такой подход, но выглядит многообещающе.См. Раздел Отображение файлов размером более 2 ГБ

http://www.kdgregory.com/index.php?page=java.byteBuffer

public int getInt(long index)
{
    return buffer(index).getInt();
}

private ByteBuffer buffer(long index)
{
    ByteBuffer buf = _buffers[(int)(index / _segmentSize)];
    buf.position((int)(index % _segmentSize));
    return buf;
}
public MappedFileBuffer(File file, int segmentSize, boolean readWrite)
throws IOException
{
    if (segmentSize > MAX_SEGMENT_SIZE)
        throw new IllegalArgumentException(
                "segment size too large (max " + MAX_SEGMENT_SIZE + "): " + segmentSize);

    _segmentSize = segmentSize;
    _fileSize = file.length();

    RandomAccessFile mappedFile = null;
    try
    {
        String mode = readWrite ? "rw" : "r";
        MapMode mapMode = readWrite ? MapMode.READ_WRITE : MapMode.READ_ONLY;

        mappedFile = new RandomAccessFile(file, mode);
        FileChannel channel = mappedFile.getChannel();

        _buffers = new MappedByteBuffer[(int)(_fileSize / segmentSize) + 1];
        int bufIdx = 0;
        for (long offset = 0 ; offset < _fileSize ; offset += segmentSize)
        {
            long remainingFileSize = _fileSize - offset;
            long thisSegmentSize = Math.min(2L * segmentSize, remainingFileSize);
            _buffers[bufIdx++] = channel.map(mapMode, offset, thisSegmentSize);
        }
    }
    finally
    {
        // close quietly
        if (mappedFile != null)
        {
            try
            {
                mappedFile.close();
            }
            catch (IOException ignored) { /* */ }
        }
    }
}
...