1 module google.protobuf.encoding; 2 3 import std.algorithm : map; 4 import std.range : chain, ElementType, empty; 5 import std.traits : isArray, isAssociativeArray, isBoolean, isFloatingPoint, isIntegral, ValueType; 6 import google.protobuf.common; 7 import google.protobuf.internal; 8 9 auto toProtobuf(T)(T value) 10 if (isBoolean!T) 11 { 12 return value ? [cast(ubyte) 0x01] : [cast(ubyte) 0x00]; 13 } 14 15 unittest 16 { 17 import std.array : array; 18 19 assert(true.toProtobuf.array == [0x01]); 20 assert(false.toProtobuf.array == [0x00]); 21 } 22 23 auto toProtobuf(Wire wire = Wire.none, T)(T value) 24 if (isIntegral!T) 25 { 26 static if (wire == Wire.none) 27 { 28 return Varint(value); 29 } 30 else static if (wire == Wire.fixed) 31 { 32 return value.encodeFixed; 33 } 34 else static if (wire == Wire.zigzag) 35 { 36 return Varint(zigZag(value)); 37 } 38 else 39 { 40 assert(0, "Invalid wire encoding"); 41 } 42 } 43 44 unittest 45 { 46 import std.array : array; 47 48 assert(10.toProtobuf.array == [0x0a]); 49 assert((-1).toProtobuf.array == [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01]); 50 assert((-1L).toProtobuf.array == [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01]); 51 assert(0xffffffffffffffffUL.toProtobuf.array == [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01]); 52 53 assert(1L.toProtobuf!(Wire.fixed).array == [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); 54 assert(1.toProtobuf!(Wire.fixed).array == [0x01, 0x00, 0x00, 0x00]); 55 assert((-1).toProtobuf!(Wire.fixed).array == [0xff, 0xff, 0xff, 0xff]); 56 assert(0xffffffffU.toProtobuf!(Wire.fixed).array == [0xff, 0xff, 0xff, 0xff]); 57 58 assert(1.toProtobuf!(Wire.zigzag).array == [0x02]); 59 assert((-1).toProtobuf!(Wire.zigzag).array == [0x01]); 60 assert(1L.toProtobuf!(Wire.zigzag).array == [0x02]); 61 assert((-1L).toProtobuf!(Wire.zigzag).array == [0x01]); 62 } 63 64 auto toProtobuf(T)(T value) 65 if (isFloatingPoint!T) 66 { 67 return value.encodeFixed; 68 } 69 70 unittest 71 { 72 import std.array : array; 73 74 assert((0.0).toProtobuf.array == [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); 75 assert((0.0f).toProtobuf.array == [0x00, 0x00, 0x00, 0x00]); 76 } 77 78 auto toProtobuf(T)(T value) 79 if (is(T == string) || is(T == bytes)) 80 { 81 return chain(Varint(value.length), cast(ubyte[]) value); 82 } 83 84 unittest 85 { 86 import std.array : array; 87 88 assert("abc".toProtobuf.array == [0x03, 'a', 'b', 'c']); 89 assert("".toProtobuf.array == [0x00]); 90 assert((cast(bytes) [1, 2, 3]).toProtobuf.array == [0x03, 1, 2, 3]); 91 assert((cast(bytes) []).toProtobuf.array == [0x00]); 92 } 93 94 auto toProtobuf(Wire wire = Wire.none, T)(T value) 95 if (isArray!T && !is(T == string) && !is(T == bytes)) 96 { 97 import std.range : hasLength; 98 99 static assert(hasLength!T, "Cannot encode array with unknown length"); 100 101 static if (wire == Wire.none) 102 { 103 auto result = value.map!(a => a.toProtobuf).sizedJoiner; 104 } 105 else 106 { 107 static assert(isIntegral!(ElementType!T), "Cannot specify wire format for non-integral arrays"); 108 109 auto result = value.map!(a => a.toProtobuf!wire).sizedJoiner; 110 } 111 112 return chain(Varint(result.length), result); 113 } 114 115 unittest 116 { 117 import std.array : array; 118 119 assert([false, false, true].toProtobuf.array == [0x03, 0x00, 0x00, 0x01]); 120 assert([1, 2].toProtobuf!(Wire.fixed).array == [0x08, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00]); 121 assert([1, 2].toProtobuf.array == [0x02, 0x01, 0x02]); 122 assert([-54L, 54L].toProtobuf!(Wire.zigzag).array == [0x02, 0x6b, 0x6c]); 123 } 124 125 auto toProtobuf(T)(T value) 126 if (is(T == class) || is(T == struct)) 127 { 128 import std.meta : AliasSeq; 129 import std.traits : hasMember; 130 131 static if (hasMember!(T, "toProtobuf")) 132 { 133 return value.toProtobuf; 134 } 135 else static if (is(Message!T.fields == AliasSeq!())) 136 { 137 return cast(ubyte[]) null; 138 } 139 else 140 { 141 import std.algorithm : joiner; 142 import std.array : array; 143 144 enum fieldExpressions = [Message!T.fieldNames] 145 .map!(a => "value.toProtobufByField!(T." ~ a ~ ")") 146 .joiner(", ") 147 .array; 148 enum resultExpression = "chain(" ~ fieldExpressions ~ ")"; 149 150 static if (isRecursive!T) 151 { 152 static if (is(T == class)) 153 { 154 if (value is null) 155 return cast(SizedRange!ubyte) sizedRangeObject(cast(ubyte[]) null); 156 } 157 158 return cast(SizedRange!ubyte) mixin(resultExpression).sizedRangeObject; 159 } 160 else 161 { 162 static if (is(T == class)) 163 { 164 if (value is null) 165 return typeof(mixin(resultExpression)).init; 166 } 167 168 return mixin(resultExpression); 169 } 170 } 171 } 172 173 unittest 174 { 175 import std.array : array; 176 177 class Foo 178 { 179 @Proto(1) int bar = protoDefaultValue!int; 180 @Proto(3) bool qux = protoDefaultValue!bool; 181 @Proto(2, Wire.fixed) long baz = protoDefaultValue!long; 182 @Proto(4) string quux = protoDefaultValue!string; 183 184 @Proto(5) Foo recursion = protoDefaultValue!Foo; 185 } 186 187 Foo foo; 188 assert(foo.toProtobuf.empty); 189 foo = new Foo; 190 assert(foo.toProtobuf.empty); 191 foo.bar = 5; 192 foo.baz = 1; 193 foo.qux = true; 194 assert(foo.toProtobuf.array == [0x08, 0x05, 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01]); 195 } 196 197 unittest 198 { 199 import std.array : array; 200 201 struct EmptyMessage 202 { 203 } 204 205 EmptyMessage emptyMessage; 206 assert(emptyMessage.toProtobuf.empty); 207 } 208 209 unittest 210 { 211 import std.array : array; 212 import std.typecons : Yes; 213 214 struct Foo 215 { 216 @Proto(1) int[] bar = protoDefaultValue!(int[]); 217 @Proto(2, Wire.zigzag, Yes.packed) int[] baz = protoDefaultValue!(int[]); 218 } 219 220 Foo foo; 221 assert(foo.toProtobuf.empty); 222 foo.bar = [1, 2]; 223 foo.baz = [3, 4]; 224 assert(foo.toProtobuf.array == [0x08, 0x01, 0x08, 0x02, 0x12, 0x02, 0x06, 0x08]); 225 } 226 227 unittest 228 { 229 struct Foo 230 { 231 @Proto(1) int bar = protoDefaultValue!int; 232 233 auto toProtobuf() 234 { 235 return [0x08, 0x00]; 236 } 237 } 238 239 Foo foo; 240 assert(foo.toProtobuf == [0x08, 0x00]); 241 } 242 243 private static auto toProtobufByField(alias field, T)(T message) 244 { 245 import std.meta : Alias; 246 247 static assert(is(Alias!(__traits(parent, field)) == T), "Field and message are different types"); 248 static assert(validateProto!(protoByField!field, typeof(field))); 249 enum proto = protoByField!field; 250 enum fieldName = __traits(identifier, field); 251 enum fieldInstanceName = "message." ~ fieldName; 252 253 static if (isOneof!field) 254 { 255 auto oneofCase = __traits(getMember, message, oneofCaseFieldName!field); 256 enum fieldCase = "T." ~ typeof(oneofCase).stringof ~ "." ~ oneofAccessorName!field; 257 258 if (oneofCase != mixin(fieldCase)) 259 return emptySizedRange!(typeof(mixin(fieldInstanceName).toProtobufByProto!proto)); 260 } 261 else 262 { 263 if (mixin(fieldInstanceName) == protoDefaultValue!(typeof(field))) 264 return emptySizedRange!(typeof(mixin(fieldInstanceName).toProtobufByProto!proto)); 265 } 266 267 return mixin(fieldInstanceName).toProtobufByProto!proto; 268 } 269 270 unittest 271 { 272 import std.array : array; 273 import std.typecons : Yes; 274 275 struct Foo 276 { 277 @Proto(1) int f10 = 10; 278 @Proto(16) int f11 = 10; 279 @Proto(2048) bool f12 = true; 280 @Proto(262144) bool f13 = true; 281 282 @Proto(20) bool f20 = false; 283 @Proto(21) int f21 = 0; 284 @Proto(22, Wire.fixed) int f22 = 0; 285 @Proto(23, Wire.zigzag) int f23 = 0; 286 @Proto(24) long f24 = 0L; 287 @Proto(25) double f25 = 0.0; 288 @Proto(26) string f26 = ""; 289 @Proto(27) bytes f27 = []; 290 291 @Proto(30) int[] f30 = [1, 2]; 292 @Proto(31, Wire.none, Yes.packed) int[] f31 = [1, 2]; 293 @Proto(32, Wire.none, Yes.packed) int[] f32 = [128, 2]; 294 } 295 296 Foo foo; 297 298 assert(foo.toProtobufByField!(Foo.f10).array == [0x08, 0x0a]); 299 assert(foo.toProtobufByField!(Foo.f11).array == [0x80, 0x01, 0x0a]); 300 assert(foo.toProtobufByField!(Foo.f12).array == [0x80, 0x80, 0x01, 0x01]); 301 assert(foo.toProtobufByField!(Foo.f13).array == [0x80, 0x80, 0x80, 0x01, 0x01]); 302 303 assert(foo.toProtobufByField!(Foo.f20).empty); 304 assert(foo.toProtobufByField!(Foo.f21).empty); 305 assert(foo.toProtobufByField!(Foo.f22).empty); 306 assert(foo.toProtobufByField!(Foo.f23).empty); 307 assert(foo.toProtobufByField!(Foo.f24).empty); 308 assert(foo.toProtobufByField!(Foo.f25).empty); 309 assert(foo.toProtobufByField!(Foo.f26).empty); 310 assert(foo.toProtobufByField!(Foo.f27).empty); 311 312 assert(foo.toProtobufByField!(Foo.f30).array == [0xf0, 0x01, 0x01, 0xf0, 0x01, 0x02]); 313 assert(foo.toProtobufByField!(Foo.f31).array == [0xfa, 0x01, 0x02, 0x01, 0x02]); 314 assert(foo.toProtobufByField!(Foo.f32).array == [0x82, 0x02, 0x03, 0x80, 0x01, 0x02]); 315 } 316 317 private auto toProtobufByProto(Proto proto, T)(T value) 318 if (isBoolean!T || 319 isFloatingPoint!T || 320 is(T == string) || 321 is(T == bytes) || 322 (isArray!T && proto.packed)) 323 { 324 static assert(validateProto!(proto, T)); 325 326 static if (proto.wire == Wire.none) 327 { 328 return chain(encodeTag!(proto, T), value.toProtobuf); 329 } 330 else 331 { 332 return chain(encodeTag!(proto, T), value.toProtobuf!(proto.wire)); 333 } 334 } 335 336 private auto toProtobufByProto(Proto proto, T)(T value) 337 if (isIntegral!T) 338 { 339 static assert(validateProto!(proto, T)); 340 341 enum wire = proto.wire; 342 return chain(encodeTag!(proto, T), value.toProtobuf!wire); 343 } 344 345 private auto toProtobufByProto(Proto proto, T)(T value) 346 if (isArray!T && !proto.packed && !is(T == string) && !is(T == bytes)) 347 { 348 static assert(validateProto!(proto, T)); 349 350 enum elementProto = Proto(proto.tag, proto.wire); 351 return value 352 .map!(a => a.toProtobufByProto!elementProto) 353 .sizedJoiner; 354 } 355 356 private auto toProtobufByProto(Proto proto, T)(T value) 357 if (isAssociativeArray!T) 358 { 359 static assert(validateProto!(proto, T)); 360 361 enum keyProto = Proto(MapFieldTag.key, keyWireToWire(proto.wire)); 362 enum valueProto = Proto(MapFieldTag.value, valueWireToWire(proto.wire)); 363 364 return value 365 .byKeyValue 366 .map!(a => chain(a.key.toProtobufByProto!keyProto, a.value.toProtobufByProto!valueProto)) 367 .map!(a => chain(encodeTag!(proto, T), Varint(a.length), a)) 368 .sizedJoiner; 369 } 370 371 unittest 372 { 373 import std.array : array; 374 import std.typecons : Yes; 375 376 assert([1, 2].toProtobufByProto!(Proto(1)).array == [0x08, 0x01, 0x08, 0x02]); 377 assert((int[]).init.toProtobufByProto!(Proto(1)).empty); 378 assert([1, 2].toProtobufByProto!(Proto(1, Wire.none, Yes.packed)).array == [0x0a, 0x02, 0x01, 0x02]); 379 assert([128, 2].toProtobufByProto!(Proto(1, Wire.none, Yes.packed)).array == [0x0a, 0x03, 0x80, 0x01, 0x02]); 380 assert((int[]).init.toProtobufByProto!(Proto(1, Wire.none, Yes.packed)).array == [0x0a, 0x00]); 381 382 assert((int[int]).init.toProtobufByProto!(Proto(1)).empty); 383 assert([1: 2].toProtobufByProto!(Proto(1)).array == [0x0a, 0x04, 0x08, 0x01, 0x10, 0x02]); 384 assert([1: 2].toProtobufByProto!(Proto(1, Wire.fixedValue)).array == 385 [0x0a, 0x07, 0x08, 0x01, 0x15, 0x02, 0x00, 0x00, 0x00]); 386 } 387 388 private auto toProtobufByProto(Proto proto, T)(T value) 389 if (is(T == class) || is(T == struct)) 390 { 391 static assert(validateProto!(proto, T)); 392 393 auto encodedValue = value.toProtobuf; 394 auto result = chain(encodeTag!(proto, T), Varint(encodedValue.length), encodedValue); 395 396 static if (isRecursive!T) 397 { 398 return cast(SizedRange!ubyte) result.sizedRangeObject; 399 } 400 else 401 { 402 return result; 403 } 404 }