From b4d915bba135f74fb38aa2872893e7ffa6b54e32 Mon Sep 17 00:00:00 2001 From: David Buckley Date: Thu, 8 Apr 2021 16:48:03 +0100 Subject: [PATCH] Decode List(Text) encoded by other tools Without this, a `List(Text)` (which is `List(List(Int8))`) which is not encodeded as `List(struct { x @0 :Text; })`, will decode the first text pointer as a tag and get very wrong answers. The patch: * Makes sure we give a sensible error for unsupported types. * Provides better checking that data is correctly formatted. * Also adds support for decoding `List(Text)` correctly, as well as decoding `List(struct {})` which was upgraded from `List(Int64)` or `List(AnyPointer)` (or compatible types). We still don't support double lists. This relates to Issue #3 (which relies on the upgrade decoding for its `List(Text)` encoding to be valid). --- src/capnp_compile.erl | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/src/capnp_compile.erl b/src/capnp_compile.erl index a6cac8a..b25c6bf 100644 --- a/src/capnp_compile.erl +++ b/src/capnp_compile.erl @@ -533,18 +533,40 @@ generate_follow_struct_list_pointer() -> -ast_forms_function(#{name => ast_follow_struct_list_pointer}). follow_tagged_struct_list_pointer(_DecodeFun, 0, _MessageRef) -> undefined; - follow_tagged_struct_list_pointer(DecodeFun, PointerInt, MessageRef) when PointerInt band 3 == 1 -> + follow_tagged_struct_list_pointer(DecodeFun, PointerInt, MessageRef) + when PointerInt band 3 == 1 andalso (PointerInt bsr 32) band 7 >= 5 -> PointerOffset = case PointerInt band (1 bsl 31) of 0 -> ((PointerInt bsr 2) band (1 bsl 30 - 1)) + 1; _ -> ((PointerInt bsr 2) band (1 bsl 30 - 1)) - (1 bsl 30) + 1 end, + LengthFromPointer = (PointerInt bsr 35) band (1 bsl 28), NewOffset = MessageRef#message_ref.current_offset + PointerOffset, - SkipBits = NewOffset bsl 6, - <<_:SkipBits, Tag:64/little-unsigned-integer, _/binary>> = MessageRef#message_ref.current_segment, - Length = ((Tag bsr 2) band (1 bsl 30 - 1)), - DWords = (Tag bsr 32) band (1 bsl 16 - 1), - PWords = (Tag bsr 48) band (1 bsl 16 - 1), - decode_struct_list(DecodeFun, Length, DWords, PWords, MessageRef#message_ref{current_offset=NewOffset+1}); + case (PointerInt bsr 32) band 7 of + 5 -> + % Pointer to a list of primitive words. + Length = LengthFromPointer, + DWords = 1, + PWords = 0, + ListStartOffset = NewOffset; + 6 -> + % Pointer to a list of pointers. + Length = LengthFromPointer, + DWords = 0, + PWords = 1, + ListStartOffset = NewOffset; + 7 -> + SkipBits = NewOffset bsl 6, + <<_:SkipBits, Tag:64/little-unsigned-integer, _/binary>> = MessageRef#message_ref.current_segment, + 1 = (Tag band 3), % Sanity check. + Length = ((Tag bsr 2) band (1 bsl 30 - 1)), + DWords = (Tag bsr 32) band (1 bsl 16 - 1), + PWords = (Tag bsr 48) band (1 bsl 16 - 1), + ListStartOffset = NewOffset + 1 + end, + decode_struct_list(DecodeFun, Length, DWords, PWords, MessageRef#message_ref{current_offset=ListStartOffset}); + follow_tagged_struct_list_pointer(DecodeFun, _PointerInt, _MessageRef) + when PointerInt band 3 == 1 andalso (PointerInt bsr 32) band 7 < 5 -> + erlang:error({not_supported, "cannot currently decode List(struct {}) which is stored as a list of subword values"}); follow_tagged_struct_list_pointer(DecodeFun, PointerInt, MessageRef=#message_ref{}) when PointerInt band 3 == 2 -> {NewPointerInt, NewMessageRef} = decode_far_pointer(PointerInt, MessageRef), follow_tagged_struct_list_pointer(DecodeFun, NewPointerInt, NewMessageRef).