-
Notifications
You must be signed in to change notification settings - Fork 265
/
Views.dfy
127 lines (108 loc) · 3.51 KB
/
Views.dfy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/*******************************************************************************
* Copyright by the contributors to the Dafny Project
* SPDX-License-Identifier: MIT
*******************************************************************************/
/**
Implements byte strings whose bounds are representable as `int32` native integers (`View`s).
*/
module Std.JSON.Utils.Views.Core {
import opened BoundedInts
type View = v: View_ | v.Valid? witness View([], 0, 0)
datatype View_ = View(s: bytes, beg: uint32, end: uint32) {
ghost const Valid?: bool :=
0 <= beg as int <= end as int <= |s| < TWO_TO_THE_32
static const Empty: View :=
View([], 0, 0)
const Empty? :=
beg == end
function Length(): uint32 requires Valid? {
end - beg
}
function Bytes(): bytes requires Valid? {
s[beg..end]
}
static function OfBytes(bs: bytes) : (v: View)
requires |bs| < TWO_TO_THE_32
ensures v.Bytes() == bs
{
View(bs, 0 as uint32, |bs| as uint32)
}
static function OfString(s: string) : bytes
requires forall c: char | c in s :: c as int < 256
{
seq(|s|, i requires 0 <= i < |s| =>
assert s[i] in s; s[i] as byte)
}
ghost predicate SliceOf?(v': View) {
v'.s == s && v'.beg <= beg && end <= v'.end
}
ghost predicate StrictPrefixOf?(v': View) {
v'.s == s && v'.beg == beg && end < v'.end
}
ghost predicate StrictSuffixOf?(v': View) {
v'.s == s && v'.beg < beg && end == v'.end
}
predicate Byte?(c: byte)
requires Valid?
{
Bytes() == [c]
} by method {
return Length() == 1 && At(0) == c;
}
predicate Char?(c: char)
requires Valid?
requires c as int < 256
{
Byte?(c as byte)
}
ghost predicate ValidIndex?(idx: uint32) {
beg as int + idx as int < end as int
}
function At(idx: uint32) : byte
requires Valid?
requires ValidIndex?(idx)
{
s[beg + idx]
}
function Peek(): (r: opt_byte)
requires Valid?
ensures r < 0 <==> Empty?
{
if Empty? then -1
else At(0) as opt_byte
}
method CopyTo(dest: array<byte>, start: uint32 := 0)
requires Valid?
requires start as int + Length() as int <= dest.Length
requires start as int + Length() as int < TWO_TO_THE_32
modifies dest
ensures dest[start..start + Length()] == Bytes()
ensures dest[start + Length()..] == old(dest[start + Length()..])
{
for idx := 0 to Length()
invariant dest[start..start + idx] == Bytes()[..idx]
invariant dest[start + Length()..] == old(dest[start + Length()..])
{
dest[start + idx] := s[beg + idx];
}
}
}
predicate Adjacent(lv: View, rv: View) {
// Compare endpoints first to short-circuit the potentially-costly string
// comparison
&& lv.end == rv.beg
// We would prefer to use reference equality here, but doing so in a sound
// way is tricky (see chapter 9 of ‘Verasco: a Formally Verified C Static
// Analyzer’ by Jacques-Henri Jourdan for details). The runtime optimizes
// the common case of physical equality and otherwise performs a length
// check, so the worst case (checking for adjacency in two slices that have
// equal but not physically-equal contents) is hopefully not too common.
&& lv.s == rv.s
}
function Merge(lv: View, rv: View) : (v: View)
requires Adjacent(lv, rv)
ensures v.Bytes() == lv.Bytes() + rv.Bytes()
{
lv.(end := rv.end)
}
}