使用Kaitai Struct在Java中解析超过2.15 GB的文件

时间:2019-05-20 09:07:06

标签: java kaitai-struct

我正在使用Kaitai-Struct在Java中解析大型PCAP文件。每当文件大小超过Integer.MAX_VALUE个字节时,我都会遇到由基础IllegalArgumentException的大小限制引起的ByteBuffer

我没有在其他地方找到对此问题的引用,这使我相信这不是库限制,而是我使用它的方式上的错误。

由于问题是由于尝试将整个文件映射到ByteBuffer引起的,因此我认为解决方案将仅映射文件的第一个区域,并且由于数据被消耗,因此再次跳过了映射已解析的数据。

由于这是在Kaitai Struct运行时库中完成的,这意味着要编写我自己的类来扩展fom KatiaiStream并覆盖自动生成的fromFile(...)方法,这似乎并不正确方法。

从文件中自动解析出PCAP类的方法是

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

并且Kaitai Struct运行时库提供的ByteBufferKaitaiStreamByteBuffer支持。

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的最大大小限制。

我缺少一些明显的解决方法吗?它真的是在Java中实施Katiati Struct的限制吗?

2 个答案:

答案 0 :(得分:1)

此库提供了使用long偏移量的ByteBuffer实现。我没有尝试过这种方法,但是看起来很有希望。请参见映射大于2 GB的文件

部分

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) { /* */ }
        }
    }
}

答案 1 :(得分:1)

这里有两个独立的问题:

  1. 对大型文件运行Pcap.fromFile()通常不是一种非常有效的方法,因为您最终会一次性将 all 个文件解析到内存阵列中。 kaitai_struct/issues/255中提供了有关如何避免这种情况的示例。基本思想是,您希望控制如何读取每个数据包,然后以某种方式对其进行解析/计算后再处理每个数据包。

  2. Java的mmaped文件限制为
  3. 2GB。为了减轻这种情况,您可以使用替代的基于RandomAccessFile的KaitaiStream实现:RandomAccessFileKaitaiStream —可能会更慢,但应避免出现2GB的问题。