= `DataAccess` and `SizedReader` Neil Clifford :toc: macro :toclevels: 1 :css-signature: demo :toc-placement: macro :icons: font toc::[] This pair of interfaces is configured using `ChronicleMapBuilder.keyReaderAndDataAccess()` or `valueReaderAndDataAccess()` for the key, or value type, of the map respectively. The reader part, `SizedReader`, is the same as in <> pair. `DataAccess` is an "advanced" interface to replace `SizedWriter`. The main method in `DataAccess` is `Data getData(@NotNull T instance)`. It returns a `Data` accessor which is used to write a "serialized" form of the instance to off-heap memory. `Data.size()` on the returned `Data` object is used for the same purpose as the `SizedWriter.size()` method in the <> pair interfaces. `Data.writeTo()` is used instead of `SizedWriter.write()`. `DataAccess` assumes that the `Data` object, returned from the `getData()` method is cached in some way. That is why it also has an `uninit()` method to clear references to the serialized object after a query operation to a Chronicle Map is complete to prevent memory leaks. This, in turn, implies that the `DataAccess` implementation is stateful. Therefore, `DataAccess` is made a sub-interface of `StatefulCopyable` to force all `DataAccess` implementations to implement `StatefulCopyable` as well. See <> for more infromation on this. If your `DataAccess` implementation is not actually stateful, it is free to return `this` from the `StatefulCopyable.copy()` method. The `DataAccess` interface is primarily intended for "serializing" objects that are already sequences of bytes, and in fact do not require serialization; for example, `byte[]`, `ByteBuffer`, arrays of Java primitives. For such types of objects, `DataAccess` allows bypassing of the intermediate buffering, copying data directly from objects to Chronicle Map's off-heap memory. For example, look at the `DataAccess` implementation for `byte[]`: ```java public final class ByteArrayDataAccess extends AbstractData implements DataAccess { /** Cache field */ private transient HeapBytesStore bs; /** State field */ private transient byte[] array; public ByteArrayDataAccess() { initTransients(); } private void initTransients() { bs = HeapBytesStore.uninitialized(); } @Override public RandomDataInput bytes() { return bs; } @Override public long offset() { return bs.start(); } @Override public long size() { return bs.capacity(); } @Override public byte[] get() { return array; } @Override public byte[] getUsing(@Nullable byte[] using) { if (using == null || using.length != array.length) using = new byte[array.length]; System.arraycopy(array, 0, using, 0, array.length); return using; } @Override public Data getData(@NotNull byte[] instance) { array = instance; bs.init(instance); return this; } @Override public void uninit() { array = null; bs.uninit(); } @Override public DataAccess copy() { return new ByteArrayDataAccess(); } @Override public void writeMarshallable(@NotNull WireOut wireOut) { // no fields to write } @Override public void readMarshallable(@NotNull WireIn wireIn) { // no fields to read initTransients(); } } ``` The `getData()` method returns `this`, and the `DataAccess` implementation implements the `Data` interface as well. This is recommended practice, because it reduces the number of objects involved (hence pointer chasing), and keeps `DataAccess`, and `Data` logic together. The `Data` interface puts constraints on `equals()`, `hashCode()`, and `toString()` implementations. This is why `ByteArrayDataAccess` sub-class'es `AbstractData`, and inherits proper implementations from it. A serializer strategy implementation can have `equals()`, `hashCode()`, and `toString()` from a very different domain, because those methods are never called on serializers inside Chronicle Map. The easiest way to implement `equals()`, `hashCode()`, and `toString()` is to extend the `AbstractData` class. If it is not possible (perhaps the `Data` implementation already extends some other class), do this by delegating to `dataEquals()`, `dataHashCode()`, and `dataToString()` default methods, provided in the `Data` interface. Corresponding `SizedReader` for `byte[]`: ```java public final class ByteArraySizedReader implements SizedReader, Marshallable, ReadResolvable { public static final ByteArraySizedReader INSTANCE = new ByteArraySizedReader(); private ByteArraySizedReader() {} @NotNull @Override public byte[] read(@NotNull Bytes in, long size, @Nullable byte[] using) { if (size < 0L || size > (long) Integer.MAX_VALUE) { throw new IORuntimeException("byte[] size should be non-negative int, " + size + " given. Memory corruption?"); } int arrayLength = (int) size; if (using == null || arrayLength != using.length) using = new byte[arrayLength]; in.read(using); return using; } @Override public void writeMarshallable(@NotNull WireOut wireOut) { // no fields to write } @Override public void readMarshallable(@NotNull WireIn wireIn) { // no fields to read } @Override public ByteArraySizedReader readResolve() { return INSTANCE; } } ``` NOTE: If you configure `byte[]` key, or value type, then this pair of serializers is used as the default. ''' <>