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 12, 2021
1 parent 0c69211 commit a82dfd9
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 23 deletions.
40 changes: 33 additions & 7 deletions src/capnp_compile.erl
Original file line number Diff line number Diff line change
Expand Up @@ -533,18 +533,44 @@ generate_follow_struct_list_pointer() ->
-ast_forms_function(#{name => ast_follow_struct_list_pointer}).
follow_struct_list_pointer(_DecodeFun, 0, _MessageRef) ->
undefined;
follow_struct_list_pointer(DecodeFun, PointerInt, MessageRef) when PointerInt band 3 == 1 ->
follow_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. Treat like a list of
% structs with a single data word.
Length = LengthFromPointer,
DWords = 1,
PWords = 0,
ListStartOffset = NewOffset;
6 ->
% Pointer to a list of pointers. Treat like a list of structs
% with a single pointer word.
Length = LengthFromPointer,
DWords = 0,
PWords = 1,
ListStartOffset = NewOffset;
7 ->
% Tagged struct list. Decode the tag.
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_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_struct_list_pointer(DecodeFun, PointerInt, MessageRef=#message_ref{}) when PointerInt band 3 == 2 ->
{NewPointerInt, NewMessageRef} = decode_far_pointer(PointerInt, MessageRef),
follow_struct_list_pointer(DecodeFun, NewPointerInt, NewMessageRef).
Expand Down
44 changes: 36 additions & 8 deletions src/capnp_schema.erl
Original file line number Diff line number Diff line change
Expand Up @@ -3181,27 +3181,55 @@ follow_data_pointer(PointerInt, MessageRef) ->
follow_struct_list_pointer(_DecodeFun, 0, _MessageRef) ->
undefined;
follow_struct_list_pointer(DecodeFun, PointerInt, MessageRef)
when PointerInt band 3 == 1 ->
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),
case (PointerInt bsr 32) band 7 of
5 ->
Length = LengthFromPointer,
DWords = 1,
PWords = 0,
ListStartOffset = NewOffset;
6 ->
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,
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),
ListStartOffset = NewOffset + 1
end,
decode_struct_list(DecodeFun,
Length,
DWords,
PWords,
MessageRef#message_ref{current_offset =
NewOffset + 1});
ListStartOffset});
follow_struct_list_pointer(_DecodeFun, PointerInt, _MessageRef)
when
PointerInt band 3 == 1
andalso
(PointerInt bsr 32) band 7 < 5 ->
error({not_supported,
"cannot currently decode List(struct {}) which is stored as "
"a list of subword values"});
follow_struct_list_pointer(DecodeFun,
PointerInt,
MessageRef = #message_ref{})
Expand Down
44 changes: 36 additions & 8 deletions src/erlcapnp_test1.erl
Original file line number Diff line number Diff line change
Expand Up @@ -2059,27 +2059,55 @@ follow_int8_list_pointer(PointerInt, MessageRef)
follow_struct_list_pointer(_DecodeFun, 0, _MessageRef) ->
undefined;
follow_struct_list_pointer(DecodeFun, PointerInt, MessageRef)
when PointerInt band 3 == 1 ->
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),
case (PointerInt bsr 32) band 7 of
5 ->
Length = LengthFromPointer,
DWords = 1,
PWords = 0,
ListStartOffset = NewOffset;
6 ->
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,
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),
ListStartOffset = NewOffset + 1
end,
decode_struct_list(DecodeFun,
Length,
DWords,
PWords,
MessageRef#message_ref{current_offset =
NewOffset + 1});
ListStartOffset});
follow_struct_list_pointer(_DecodeFun, PointerInt, _MessageRef)
when
PointerInt band 3 == 1
andalso
(PointerInt bsr 32) band 7 < 5 ->
error({not_supported,
"cannot currently decode List(struct {}) which is stored as "
"a list of subword values"});
follow_struct_list_pointer(DecodeFun,
PointerInt,
MessageRef = #message_ref{})
Expand Down

0 comments on commit a82dfd9

Please sign in to comment.