获取相对于原始ByteBuffer的直接ByteBuffer片段位置

4

如果已知一个ByteBuffer是另一个缓冲区的子序列,是否有办法仅通过这两个ByteBuffer对象获取直接ByteBuffer零位置相对于另一个的位置?

我知道可以使用arrayOffset()方法来处理非直接数组支持的ByteBuffer,如下所示:

int getRelativeBufferOffset(ByteBuffer parentBuffer, ByteBuffer childBuffer)
{
  return childBuffer.arrayOffset() - parentBuffer.arrayOffset();
}

void example()
{
  ByteBuffer buffer1 = ByteBuffer.allocate(10000);
  buffer1 .position(22);
  ByteBuffer buffer2 = buffer1.slice();
  buffer2.position(55);
  ByteBuffer buffer3 = buffer2.slice();

  // returns 22
  getRelativeBufferOffset(buffer1, buffer2);

  // returns 55
  getRelativeBufferOffset(buffer2, buffer3);

  // returns 77
  getRelativeBufferOffset(buffer1, buffer3);
}

我认为没有免费的直接缓冲区存在。为了获得类似的东西,我能想到的最好的选择是扩展 ByteBuffer ,以存储对创建它(父缓冲区)的引用和相对于父缓冲区零位置创建时的零位置。
编辑:令人恼火的是,看起来我无法扩展 ByteBuffer ,因为它的构造函数都不可见。我想我必须编写某种包装类。
1个回答

1
使用反射是可能的。但是根据您想要实现的目标,您应该考虑替代方案。不清楚您需要此偏移量的目的。 "务实" 的建议是将缓冲区包装到一个简单的自己的类中,例如:
class SlicedBuffer {
    int getBuffer() { ... }
    Buffer getParent() { ... }
    int getOffsetToParent() { ... }
}

我希望你能与这个一起工作,但不清楚这是否适用于你的情况。

我将在这里使用反射发布代码,但请注意

// Many things...
// ... can go ...
// ... wrong when...
// ... using reflection

所以这只是一个演示:
import java.lang.reflect.Field;
import java.nio.Buffer;
import java.nio.ByteBuffer;

public class DirectByteBufferSliceOffsetsTest
{
    public static void main(String[] args)
    {
        testArray();
        testDirect();
    }

    private static void testArray()
    {
        System.out.println("Array: ");

        ByteBuffer buffer1 = ByteBuffer.allocate(10000);
        buffer1.position(22);
        ByteBuffer buffer2 = buffer1.slice();
        buffer2.position(55);
        ByteBuffer buffer3 = buffer2.slice();

        // prints 22
        System.out.println(getRelativeBufferOffsetArray(buffer1, buffer2));

        // prints 55
        System.out.println(getRelativeBufferOffsetArray(buffer2, buffer3));

        // prints 77
        System.out.println(getRelativeBufferOffsetArray(buffer1, buffer3));
    }

    private static int getRelativeBufferOffsetArray(
        ByteBuffer parentBuffer, ByteBuffer childBuffer)
    {
        return childBuffer.arrayOffset() - parentBuffer.arrayOffset();
    }


    private static void testDirect()
    {
        System.out.println("Direct: ");

        ByteBuffer buffer1 = ByteBuffer.allocateDirect(10000);
        buffer1.position(22);
        ByteBuffer buffer2 = buffer1.slice();
        buffer2.position(55);
        ByteBuffer buffer3 = buffer2.slice();

        // prints 22
        System.out.println(getRelativeBufferOffsetDirect(buffer1, buffer2));

        // prints 55
        System.out.println(getRelativeBufferOffsetDirect(buffer2, buffer3));

        // prints 77
        System.out.println(getRelativeBufferOffsetDirect(buffer1, buffer3));
    }

    private static int getRelativeBufferOffsetDirect(
        ByteBuffer parentBuffer, ByteBuffer childBuffer)
    {
        long parentAddress = getAddress(parentBuffer);
        long childAddress = getAddress(childBuffer);
        int offset = (int)(childAddress - parentAddress);
        return offset;
    }

    private static long getAddress(Buffer buffer)
    {
        Field f = null;
        try
        {
            f = Buffer.class.getDeclaredField("address");
            f.setAccessible(true);
            return f.getLong(buffer);
        }
        catch (NoSuchFieldException e)
        {
            // Many things...
            e.printStackTrace();
        }
        catch (SecurityException e)
        {
            // ... can go ...
            e.printStackTrace();
        }
        catch (IllegalArgumentException e)
        {
            // ... wrong when...
            e.printStackTrace();
        }
        catch (IllegalAccessException e)
        {
            // ... using reflection
            e.printStackTrace();
        }
        finally
        {
            if (f != null)
            {
                f.setAccessible(false);
            }
        }
        return 0;
    }
}

反射解决方案很好,但出于安全考虑,我认为我会选择包装器路线,并将ByteBuffer封装在RelativeByteBuffer类中。不幸的是,Buffer没有公开duplicate()slice()方法,因此我无法像RelativeBuffer<T extends Buffer>那样做一些巧妙的事情。 - PaddyD
偏移量是用于诊断日志记录的,当在缓冲区中遇到意外数据时 - 我需要知道相对于创建缓冲区的另一个缓冲区的数据偏移量。 - PaddyD
我认为仅用于诊断目的,反射解决方案应该是可以的。我不确定进行这种分析的意图应该如何影响首先对类进行建模的方式。但最终决定取决于您。也许其他人会写出更适合您需求的答案。 - Marco13
我将把这标记为答案,因为你涵盖了所有方面。感谢你的帮助。 - PaddyD

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接