Skip to content
Ishinka edited this page May 9, 2022 · 2 revisions

This is a Java implementation of the Substrate SCALE Codec.

The SCALE (Simple Concatenated Aggregate Little-Endian) Codec is a lightweight, efficient, binary serialization and deserialization codec.

SCALE is used in many different ways:

  • serialization of parameters in RPC methods and subscriptions;
  • deserialization of results in RPC methods and subscriptions;
  • serialization of parameters in transactions;
  • serialization of keys in storage items;
  • deserialization values of storage items.

This component includes abstractions for (de-)serialization entities from/to SCALE and tools which help to generate corresponding (de-)serializers, which we call scale readers and scale writers.

Registries

Scale readers and writers are stored in two corresponding registries ScaleReaderRegistry and ScaleWriterRegistry. These registries are singletons implementing the following methods:

  • register() used for reader/writer registration
  • resolve() resolves a previously registered reader/writer by a class
  • registerAnnotatedFrom() to automatically register all the readers/writers marked by @AutoRegister annotation.

By default registries come with support for the following types:

  • Boolean
  • Byte
  • Short
  • Integer
  • Long
  • BigInteger
  • Optional
  • Result
  • String
  • List
  • Void

Annotations

@AutoRegister

This annotation is used to enable the autoregistration feature in case of a manual implementation of a reader/writer.

@AutoRegister(types = MyClass.class)
public class MyClassScaleReader implements ScaleReader<MyClass> {
    @Override
    public MyClass read(@NonNull InputStream stream, ScaleReader<?>... readers) throws IOException {
//        ...
    }
}

@ScaleReader, @ScaleWriter

The @ScaleReader, @ScaleWriter annotations are used to automatically generate SCALE readers/writers for models marked with it. If the proper readers/writers can be resolved from the registries using the types of the fields, then no extra annotations are needed.

@ScaleReader
@ScaleWriter
public class MyModel<T> {
    private Boolean someBool;
    public Boolean getSomeBool() {
        return someBool;
    }

    public void setSomeBool(Boolean someBool) {
        this.someBool = someBool;
    }    

    private Optional<T> someOption;
    public Optional<T> getSomeOption() {
        return someOption;
    }

    public void setSomeOption(Optional<T> someOption) {
        this.someOption = someOption;
    }
}

Readers/writers generated this way are marked with @AutoRegister automatically.

@Scale, @ScaleGeneric, @Ignore

The @Scale, @ScaleGeneric and @Ignore annotations are used on a model's fields to provide hints for the automatic generation of readers/writers.

  • @Scale specifies a particular reader/writer that will be resolved from the registry for a marked field. If used without arguments, the type of the marked field is used.
  • @ScaleGeneric specifies particular readers/writers for the fields with generic types.
  • @Ignore tells the generator to skip the marked field during the SCALE encoding/decoding process.
@ScaleReader
public class MyModel<T> {
    @Scale(OptionBool.class)
    private Optional<Boolean> optionBool;

    @Scale(CompactInteger.class)
    private Integer compactInteger;

    @Scale
    private T obj;

    @ScaleGeneric(
        template = "Option<I32>",
        types = {
                @Scale(Option.class),
                @Scale(I32.class)
        })
    private Optional<Integer> optionI32;

    @ScaleGeneric(
            template = "Map<Vec<?>, Result<OptionBool, String>>",
            types = {
                    @Scale(Map.class),
                    @Scale(Vec.class),
                    @Scale(ScaleType.OptionBool.class),
                    @Scale(Result.class),
                    @Scale(String.class)
            }
    )
    private Map<List<T>, Result<Optional<Boolean>, String>> mapTypes;

    @ScaleGeneric(
            template = "map<int, result>",
            types = {
                    @Scale(value = Map.class, name = "map"),
                    @Scale(value = ScaleType.I32.class, name = "int"),
                    @Scale(name = "result"),
            }
    )
    private Map<Integer, Result<Boolean, String>> mapNames;

    @Ignore
    private String ignored;   
}
Clone this wiki locally