Skip to content

Commit

Permalink
Decode List(Text) encoded by other tools
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
bucko909 committed Apr 9, 2021
1 parent e0043e4 commit eaac305
Showing 1 changed file with 30 additions and 7 deletions.
37 changes: 30 additions & 7 deletions src/capnp_compile.erl
Original file line number Diff line number Diff line change
Expand Up @@ -533,18 +533,41 @@ 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 29 - 1),
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,
0 = (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),
LengthFromPointer = Length * (DWords + PWords), % Sanity check
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).
Expand Down

0 comments on commit eaac305

Please sign in to comment.