@@ -674,7 +674,13 @@ module DynamoToStruct {
674
674
// = specification/dynamodb-encryption-client/ddb-attribute-serialization.md#list-entry-value
675
675
// # A List MAY hold any DynamoDB Attribute Value data type,
676
676
// # and MAY hold values of different types.
677
- function method {:opaque} CollectList (
677
+
678
+ // Can't be {:tailrecursion} because it calls AttrToBytes which might again call CollectList
679
+ // However, we really need this to loop and not recurse.
680
+ // This verifies without the `by method`, but Dafny is too broken to let it verify by method
681
+ // for example, a call to CollectList somehow does not satisfy the decreases clause
682
+ // hence the {:verify false}
683
+ function {:opaque} {:verify false } {:axiom} CollectList (
678
684
listToSerialize : ListAttributeValue ,
679
685
depth : uint32 ,
680
686
serialized : seq <uint8 > := []
@@ -687,10 +693,18 @@ module DynamoToStruct {
687
693
if |listToSerialize| == 0 then
688
694
Success (serialized)
689
695
else
690
- // Can't do the `pos` optimization, because I can't appease the `decreases`
691
696
var val :- AttrToBytes (listToSerialize[0], true, depth+1);
692
697
CollectList (listToSerialize[1..], depth, serialized + val)
693
698
}
699
+ by method {
700
+ var result := serialized;
701
+ for i := 0 to |listToSerialize|
702
+ {
703
+ var val :- AttrToBytes (listToSerialize[i], true, depth+1);
704
+ result := result + val;
705
+ }
706
+ return Success (result);
707
+ }
694
708
695
709
function method SerializeMapItem (key : string , value : seq <uint8 >) : (ret : Result< seq < uint8> , string > )
696
710
@@ -892,31 +906,23 @@ module DynamoToStruct {
892
906
DeserializeNumberSet (serialized[len..], remainingCount-1, origSerializedSize, AttrValueAndLength(nattr, resultSet.len + len + LENGTH_LEN32))
893
907
}
894
908
895
- // Bytes to List
896
- // Can't be {:tailrecursion} because it calls BytesToAttr which might again call DeserializeList
897
- function method {:vcs_split_on_every_assert} {:opaque} DeserializeList (
909
+ function method {:vcs_split_on_every_assert} DeserializeListEntry (
898
910
serialized : seq <uint8 >,
899
911
pos : uint32 ,
900
- ghost orig_pos : uint32 ,
901
- remainingCount : uint32 ,
902
912
depth : uint32 ,
903
913
resultList : AttrValueAndLength
904
914
)
905
- : (ret : Result< AttrValueAndLength, string > )
915
+ : (ret : Result< ( AttrValueAndLength, uint32) , string > )
906
916
requires |serialized| < UINT32_MAX as int
907
917
requires pos as int <= |serialized|
908
- requires orig_pos <= pos
909
918
requires depth <= MAX_STRUCTURE_DEPTH
910
919
requires resultList. val. L?
911
- ensures ret. Success? ==> ret. value. val. L?
912
- requires pos == Add32 (orig_pos, resultList.len)
913
- ensures ret. Success? ==> ret. value. len <= |serialized| as uint32 - orig_pos
914
- decreases |serialized| as uint32 - pos
920
+ ensures ret. Success? ==> ret. value. 0. val. L?
921
+ ensures ret. Success? ==> pos < ret. value. 1 <= |serialized| as uint32
922
+ decreases |serialized| as uint32 - pos, 0
915
923
{
916
924
var serialized_size := |serialized| as uint32;
917
- if remainingCount == 0 then
918
- Success (resultList)
919
- else if serialized_size- pos < PREFIX_LEN32 then
925
+ if serialized_size- pos < PREFIX_LEN32 then
920
926
Failure ("Out of bytes reading Type of List element")
921
927
else
922
928
var TerminalTypeId := serialized[pos.. pos+ 2];
@@ -927,9 +933,57 @@ module DynamoToStruct {
927
933
else
928
934
assert serialized_size == |serialized| as uint32;
929
935
var nval :- BytesToAttr (serialized, TerminalTypeId, Some(len), depth+ 1, new_pos);
936
+ var new_pos := new_pos + nval. len;
930
937
var nattr := AttributeValue. L (resultList.val.L + [nval.val]);
931
- var nResultList := AttrValueAndLength (nattr, Add32_3(resultList.len, len, PREFIX_LEN32));
932
- DeserializeList (serialized, new_pos+len, orig_pos, remainingCount-1, depth, nResultList)
938
+ var nResultList := AttrValueAndLength (nattr, Add32(resultList.len, new_pos-pos));
939
+ Success ((nResultList, new_pos))
940
+ }
941
+
942
+ // Bytes to List
943
+ // Can't be {:tailrecursion} because it calls BytesToAttr which might again call DeserializeList
944
+ // However, we really need this to loop and not recurse.
945
+ // This verifies without the `by method`, but Dafny is too broken to let it verify by method
946
+ // for example, a call to DeserializeListEntry somehow does not satisfy the decreases clause
947
+ // hence the {:verify false}
948
+ function {:vcs_split_on_every_assert} {:opaque} {:verify false } DeserializeList (
949
+ serialized : seq <uint8 >,
950
+ pos : uint32 ,
951
+ ghost orig_pos : uint32 ,
952
+ remainingCount : uint32 ,
953
+ depth : uint32 ,
954
+ resultList : AttrValueAndLength
955
+ )
956
+ : (ret : Result< AttrValueAndLength, string > )
957
+ requires |serialized| < UINT32_MAX as int
958
+ requires pos as int <= |serialized|
959
+ requires orig_pos <= pos
960
+ requires depth <= MAX_STRUCTURE_DEPTH
961
+ requires resultList. val. L?
962
+ ensures ret. Success? ==> ret. value. val. L?
963
+ requires pos == Add32 (orig_pos, resultList.len)
964
+ ensures ret. Success? ==> ret. value. len <= |serialized| as uint32 - orig_pos
965
+ decreases |serialized| as uint32 - pos, 1
966
+ {
967
+ if remainingCount == 0 then
968
+ Success (resultList)
969
+ else
970
+ var (newResultList, npos) :- DeserializeListEntry (serialized, pos, depth, resultList);
971
+ DeserializeList (serialized, npos, orig_pos, remainingCount - 1, depth, newResultList)
972
+ }
973
+ by method {
974
+ var npos : uint32 := pos;
975
+ var newResultList := resultList;
976
+ for i := 0 to remainingCount
977
+ invariant serialized == old (serialized)
978
+ invariant newResultList. val. L?
979
+ invariant npos as int <= |serialized|
980
+ invariant npos == Add32 (orig_pos, newResultList.len)
981
+ {
982
+ var test :- DeserializeListEntry (serialized, npos, depth, newResultList);
983
+ newResultList := test. 0;
984
+ npos := test. 1;
985
+ }
986
+ ret := Success (newResultList);
933
987
}
934
988
935
989
function method {:vcs_split_on_every_assert} DeserializeMapEntry (
@@ -990,6 +1044,7 @@ module DynamoToStruct {
990
1044
// Can't be {:tailrecursion} because it calls BytesToAttr which might again call DeserializeMap
991
1045
// However, we really need this to loop and not recurse.
992
1046
// This verifies without the `by method`, but Dafny is too broken to let it verify by method
1047
+ // for example, a call to DeserializeMapEntry somehow does not satisfy the decreases clause
993
1048
// hence the {:verify false}
994
1049
function {:vcs_split_on_every_assert} {:opaque} {:verify false } DeserializeMap (
995
1050
serialized : seq <uint8 >,
0 commit comments