--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Low-level read MessagePack format from `Reader` streams
+module read
+
+import serialization
+private import binary
+
+import ext
+
+redef class Reader
+
+ # Read the next MessagePack object and return it as a simple Nit object
+ #
+ # The return value is composed of:
+ # * the simple types `null`, `Bool`, `Int`, `Float`, `String` and `Bytes`,
+ # * collections of simple Nit objects `Array[nullable Serializable]`
+ # and `Map[nullable Serializable, nullable Serializable]`,
+ # * and `MsgPackExt` for custom MessagePack *ext* data.
+ #
+ # This method reads plain MessagePack data, as written by `MsgPackSerializer`
+ # when `plain_msgpack == true`. To deserialize full Nit objects from
+ # MessagePack with metadata use `Reader::deserialize_msgpack`.
+ fun read_msgpack: nullable Serializable
+ do
+ if last_error != null then return 0
+
+ var typ = read_byte
+ if typ == null then
+ # Error, return default `null`
+ return null
+
+ else if typ & 0b1000_0000u8 == 0u8 or typ & 0b1110_0000u8 == 0b1110_0000u8 then
+ # fixint
+ var bytes = new Bytes.with_capacity(1)
+ bytes.add typ
+ return bytes.to_i(signed=true)
+
+ else if typ & 0b1111_0000u8 == 0b1000_0000u8 then
+ # fixmap
+ var len = typ & 0b0000_1111u8
+ return read_msgpack_map_data(len.to_i)
+
+ else if typ & 0b1111_0000u8 == 0b1001_0000u8 then
+ # fixarray
+ var len = typ & 0b0000_1111u8
+ return read_msgpack_array_data(len.to_i)
+
+ else if typ & 0b1110_0000u8 == 0b1010_0000u8 then
+ # fixstr
+ var len = typ & 0b0001_1111u8
+ return read_bytes(len.to_i).to_s
+
+ else if typ == 0xC0u8 then
+ return null
+ else if typ == 0xC2u8 then
+ return false
+ else if typ == 0xC3u8 then
+ return true
+
+ else if typ == 0xCCu8 then
+ # uint8
+ return (read_byte or else 0u8).to_i
+ else if typ == 0xCDu8 then
+ # uint16
+ return read_bytes(2).to_i
+ else if typ == 0xCEu8 then
+ # uint32
+ return read_bytes(4).to_i
+ else if typ == 0xCFu8 then
+ # uint64
+ return read_bytes(8).to_i
+ else if typ == 0xD0u8 then
+ # int8
+ return read_bytes(1).to_i(true)
+ else if typ == 0xD1u8 then
+ # int16
+ return read_bytes(2).to_i(true)
+ else if typ == 0xD2u8 then
+ # int32
+ return read_bytes(4).to_i(true)
+ else if typ == 0xD3u8 then
+ # int64
+ return read_int64
+
+ else if typ == 0xCAu8 then
+ return read_float
+ else if typ == 0xCBu8 then
+ return read_double
+
+ else if typ == 0xD9u8 then
+ # str8
+ var len = read_byte
+ if len == null then return null
+ return read_bytes(len.to_i).to_s
+ else if typ == 0xDAu8 then
+ # str16
+ var len = read_bytes(2)
+ return read_bytes(len.to_i).to_s
+ else if typ == 0xDBu8 then
+ # str32
+ var len = read_bytes(4)
+ return read_bytes(len.to_i).to_s
+
+ else if typ == 0xC4u8 then
+ # bin8
+ var len = read_byte
+ if len == null then return null
+ return read_bytes(len.to_i)
+ else if typ == 0xC5u8 then
+ # bin16
+ var len = read_bytes(2)
+ return read_bytes(len.to_i)
+ else if typ == 0xC6u8 then
+ # bin32
+ var len = read_bytes(4)
+ return read_bytes(len.to_i)
+
+ else if typ == 0xDCu8 then
+ # array16
+ var len = read_bytes(2)
+ return read_msgpack_array_data(len.to_i)
+ else if typ == 0xDDu8 then
+ # array32
+ var len = read_bytes(4)
+ return read_msgpack_array_data(len.to_i)
+
+ else if typ == 0xDEu8 then
+ # map16
+ var len = read_bytes(2)
+ return read_msgpack_map_data(len.to_i)
+ else if typ == 0xDFu8 then
+ # map32
+ var len = read_bytes(4)
+ return read_msgpack_map_data(len.to_i)
+
+ else if typ == 0xD4u8 then
+ # fixext1
+ return read_msgpack_fixext_data(1)
+ else if typ == 0xD5u8 then
+ # fixext2
+ return read_msgpack_fixext_data(2)
+ else if typ == 0xD6u8 then
+ # fixext4
+ return read_msgpack_fixext_data(4)
+ else if typ == 0xD7u8 then
+ # fixext8
+ return read_msgpack_fixext_data(8)
+ else if typ == 0xD8u8 then
+ # fixext16
+ return read_msgpack_fixext_data(16)
+
+ else if typ == 0xC7u8 then
+ # ext1
+ return read_msgpack_ext_data(1)
+ else if typ == 0xC8u8 then
+ # ext2
+ return read_msgpack_ext_data(2)
+ else if typ == 0xC9u8 then
+ # ext4
+ return read_msgpack_ext_data(4)
+ end
+
+ print_error "MessagePack Warning: Found no match for typ {typ} / 0b{typ.to_i.to_base(2)}"
+ return null
+ end
+
+ # Read the content of a map, `len` keys and values
+ private fun read_msgpack_map_data(len: Int): Map[nullable Serializable, nullable Serializable]
+ do
+ var map = new Map[nullable Serializable, nullable Serializable]
+ for i in [0..len.to_i[ do map[read_msgpack] = read_msgpack
+ return map
+ end
+
+ # Read the content of an array of `len` items
+ private fun read_msgpack_array_data(len: Int): Array[nullable Serializable]
+ do
+ return [for i in [0..len[ do read_msgpack]
+ end
+
+ # Read the content of a *fixext* of `len` bytes
+ #
+ # ~~~
+ # var reader = new BytesReader(b"\xC7\x03\x0A\x0B\x0C\x0D")
+ # var ext = reader.read_msgpack
+ # assert ext isa MsgPackExt
+ # assert ext.typ == 0x0Au8
+ # assert ext.data == b"\x0B\x0C\x0D"
+ # ~~~
+ private fun read_msgpack_fixext_data(len: Int): MsgPackExt
+ do
+ var exttyp = read_byte or else 0u8
+ var data = read_bytes(len)
+ return new MsgPackExt(exttyp, data)
+ end
+
+ # Read the content of a dynamic *ext* including the length on `len_len` bytes
+ private fun read_msgpack_ext_data(len_len: Int): MsgPackExt
+ do
+ var len = read_bytes(len_len).to_i
+ return read_msgpack_fixext_data(len)
+ end
+end