36 |
36 |
37 using namespace relpipe::protocol; |
37 using namespace relpipe::protocol; |
38 using namespace relpipe::reader; |
38 using namespace relpipe::reader; |
39 |
39 |
40 /** |
40 /** |
41 * The prototype does not have various integer and other numeric data types, |
41 * Unsigned variable-length integer. |
42 * it just works with one type of integer. |
42 * ULEB128 |
43 * But this integer has variable length -- smaller values occupy only one byte, bigger ones, more bytes 1,2,4,8 + first byte (contains length signalization). |
|
44 * In the real implementation of relational pipes, there will be DataTypes for particular numeric types. |
|
45 * |
|
46 * TODO: support also big endian architectures. |
|
47 * TODO: throw exception if a value was stored in bigger type than needed (while reading – there should be only one supported way how to encode a single value) |
|
48 * |
|
49 * Example of encoded values: |
|
50 * ------------------------------------------------------------------------------------------------- |
|
51 * $ for n in 0 1 10 250 251 252 65535 65536 4294967295 4294967296 18446744073709551615; do printf '%20s = ' $n; dist/Debug/GNU-Linux/rp-prototype write integer $n | hd | head -n 1; done |
|
52 * 0 = 00000000 00 |.| |
|
53 * 1 = 00000000 01 |.| |
|
54 * 10 = 00000000 0a |.| |
|
55 * 250 = 00000000 fa |.| |
|
56 * 251 = 00000000 fb fb |..| |
|
57 * 252 = 00000000 fb fc |..| |
|
58 * 65535 = 00000000 fc ff ff |...| |
|
59 * 65536 = 00000000 fd 00 00 01 00 |.....| |
|
60 * 4294967295 = 00000000 fd ff ff ff ff |.....| |
|
61 * 4294967296 = 00000000 fe 00 00 00 00 01 00 00 00 |.........| |
|
62 * 18446744073709551615 = 00000000 fe ff ff ff ff ff ff ff ff |.........| |
|
63 * ------------------------------------------------------------------------------------------------- |
|
64 * |
|
65 * Example of decoded values: |
|
66 * ------------------------------------------------------------------------------------------------- |
|
67 * $ for n in 0 1 10 250 251 252 65535 65536 4294967295 4294967296 18446744073709551615; do dist/Debug/GNU-Linux/rp-prototype write integer $n | dist/Debug/GNU-Linux/rp-prototype read integer; done; |
|
68 * 0 |
|
69 * 1 |
|
70 * 10 |
|
71 * 250 |
|
72 * 251 |
|
73 * 252 |
|
74 * 65535 |
|
75 * 65536 |
|
76 * 4294967295 |
|
77 * 4294967296 |
|
78 * 18446744073709551615 |
|
79 * ------------------------------------------------------------------------------------------------- |
|
80 * |
|
81 * Note: similar format as original idea: https://en.wikipedia.org/wiki/X.690#Length_octets |
|
82 * |
|
83 */ |
43 */ |
84 class IntegerDataTypeReader : public DataTypeReader<integer_t> { |
44 class IntegerDataTypeReader : public DataTypeReader<integer_t> { |
85 private: |
45 private: |
86 static const uint8_t INTEGER_TYPE_UINT8 = 251; |
|
87 static const uint8_t INTEGER_TYPE_UINT16 = 252; |
|
88 static const uint8_t INTEGER_TYPE_UINT32 = 253; |
|
89 static const uint8_t INTEGER_TYPE_UINT64 = 254; |
|
90 static const uint8_t INTEGER_TYPE_RESERVED = 255; |
|
91 |
46 |
92 template<typename T> integer_t read(std::istream &input) { |
47 uint8_t readNextOctet(std::istream &input) { |
93 T value = 0; |
48 uint8_t value = input.get(); |
94 input.read(reinterpret_cast<char *> (&value), sizeof (value)); |
49 if (input.good()) return value; |
95 return value; |
50 else throw RelpipeReaderException(L"Unable to read next octet of the integer."); |
96 } |
51 } |
97 |
52 |
98 public: |
53 public: |
99 |
54 |
100 IntegerDataTypeReader() : DataTypeReader<integer_t>(TypeId::INTEGER, DATA_TYPE_CODE_INTEGER) { |
55 IntegerDataTypeReader() : DataTypeReader<integer_t>(TypeId::INTEGER, DATA_TYPE_CODE_INTEGER) { |
101 } |
56 } |
102 |
57 |
103 integer_t readValue(std::istream &input) override { |
58 integer_t readValue(std::istream &input) override { |
104 uint8_t first; |
59 integer_t value = 0; |
105 input.read(reinterpret_cast<char *> (&first), sizeof (first)); |
60 integer_t shift = 0; |
106 if (input.good()) { |
61 octet_t octet; |
107 |
62 do { |
108 if (first < INTEGER_TYPE_UINT8) return first; |
63 octet = readNextOctet(input); |
109 else if (first == INTEGER_TYPE_UINT8) return read<uint8_t>(input); |
64 value += integer_t(octet & 0x7F) << shift; |
110 else if (first == INTEGER_TYPE_UINT16) return read<uint16_t>(input); |
65 shift += 7; |
111 else if (first == INTEGER_TYPE_UINT32) return read<uint32_t>(input); |
66 } while (octet >= 128); |
112 else if (first == INTEGER_TYPE_UINT64) return read<uint64_t>(input); |
67 return value; |
113 else throw RelpipeReaderException(L"Error while parsing integer type: unsupported type"); |
|
114 } else { |
|
115 throw RelpipeReaderException(L"Error while reading integer from the stream."); |
|
116 } |
|
117 } |
68 } |
118 |
69 |
119 string_t toString(const integer_t &value) override { |
70 string_t toString(const integer_t &value) override { |
120 return std::to_wstring(value); |
71 return std::to_wstring(value); |
121 } |
72 } |