File Coverage

src/panda/protocol/websocket/Utf8Checker.cc
Criterion Covered Total %
statement 54 97 55.6
branch 32 94 34.0
condition n/a
subroutine n/a
pod n/a
total 86 191 45.0


line stmt bran cond sub pod time code
1             #include "Utf8Checker.h"
2             #include
3              
4             namespace panda { namespace protocol { namespace websocket {
5              
6 22           static bool valid (std::uint8_t const*& p) {
7 22 100         if (p[0] < 128) {
8 4           ++p;
9 4           return true;
10             }
11 18 50         if ((p[0] & 0xe0) == 0xc0) {
12 18 50         if ((p[1] & 0xc0) != 0x80
13 18 100         || (p[0] & 0xfe) == 0xc0 // overlong
14 2           ) return false;
15 16           p += 2;
16 16           return true;
17             }
18 0 0         if ((p[0] & 0xf0) == 0xe0) {
19 0 0         if ((p[1] & 0xc0) != 0x80
20 0 0         || (p[2] & 0xc0) != 0x80
21 0 0         || (p[0] == 0xe0 && (p[1] & 0xe0) == 0x80) // overlong
    0          
22 0 0         || (p[0] == 0xed && (p[1] & 0xe0) == 0xa0) // surrogate
    0          
23             //|| (p[0] == 0xef && p[1] == 0xbf && (p[2] & 0xfe) == 0xbe) // U+FFFE or U+FFFF
24             )
25 0           return false;
26 0           p += 3;
27 0           return true;
28             }
29 0 0         if ((p[0] & 0xf8) == 0xf0) {
30 0 0         if( (p[1] & 0xc0) != 0x80
31 0 0         || (p[2] & 0xc0) != 0x80
32 0 0         || (p[3] & 0xc0) != 0x80
33 0 0         || (p[0] == 0xf0 && (p[1] & 0xf0) == 0x80) // overlong
    0          
34 0 0         || (p[0] == 0xf4 && p[1] > 0x8f) || p[0] > 0xf4 // > U+10FFFF
    0          
    0          
35             )
36 0           return false;
37 0           p += 4;
38 0           return true;
39             }
40 0           return false;
41             }
42              
43 10           static size_t needed (std::uint8_t const v) {
44 10 100         if (v < 128) return 1;
45 6 50         if (v < 192) return 0;
46 6 50         if (v < 224) return 2;
47 0 0         if (v < 240) return 3;
48 0 0         if (v < 248) return 4;
49 0           return 0;
50             }
51              
52 14           bool Utf8Checker::write (const string& s) {
53 14           auto in = (const uint8_t*)s.data();
54 14           auto size = s.length();
55 14           auto const end = in + size;
56              
57 4           auto fail_fast = [&]() {
58 20           auto const n = p_ - cp_;
59 4           switch (n) {
60 0           default: assert(false);
61 4           case 1: cp_[1] = 0x81; // fallthrough
62 4           case 2: cp_[2] = 0x81; // fallthrough
63 4           case 3: cp_[3] = 0x81;
64 4           break;
65             }
66 4           std::uint8_t const* p = cp_;
67 4           return !valid(p);
68 14           };
69              
70             // Finish up any incomplete code point
71 14 100         if (need_ > 0) {
72             // Calculate what we have
73 4           auto n = (std::min)(size, (size_t)need_);
74 4           size -= n;
75 4           need_ -= n;
76              
77             // Add characters to the code point
78 8 100         while (n--) *p_++ = *in++;
79 4 50         assert(p_ <= cp_ + 4);
80              
81             // Still incomplete?
82 4 50         if (need_ > 0) {
83             // Incomplete code point
84 0 0         assert(in == end);
85              
86             // Do partial validation on the incomplete
87             // code point, this is called "Fail fast"
88             // in Autobahn|Testsuite parlance.
89 0           return ! fail_fast();
90             }
91              
92             // Complete code point, validate it
93 4           std::uint8_t const* p = &cp_[0];
94 4 50         if (!valid(p)) return false;
95 4           p_ = cp_;
96             }
97              
98 14 50         if (size <= sizeof(std::size_t)) goto slow;
99              
100             // Align `in` to sizeof(std::size_t) boundary
101             {
102 0           auto const in0 = in;
103 0           auto last = reinterpret_cast(((reinterpret_cast(in) + sizeof(std::size_t) - 1) / sizeof(std::size_t)) * sizeof(std::size_t));
104              
105             // Check one character at a time for low-ASCII
106 0 0         while (in < last) {
107 0 0         if (*in & 0x80) {
108             // Not low-ASCII so switch to slow loop
109 0           size = size - (in - in0);
110 0           goto slow;
111             }
112 0           ++in;
113             }
114 0           size = size - (in - in0);
115             }
116              
117             // Fast loop: Process 4 or 8 low-ASCII characters at a time
118             {
119 0           auto const in0 = in;
120 0           auto last = in + size - 7;
121 0           auto constexpr mask = static_cast(0x8080808080808080 & ~std::size_t{0});
122 0 0         while (in < last) {
123             // Technically UB but works on all known platforms
124 0 0         if ((*reinterpret_cast(in) & mask) != 0) {
125 0           size = size - (in - in0);
126 0           goto slow;
127             }
128 0           in += sizeof(std::size_t);
129             }
130             // There's at least one more full code point left
131 0           last += 4;
132 0 0         while (in < last) if (!valid(in)) return false;
    0          
133 0           goto tail;
134             }
135              
136             slow:
137             // Slow loop: Full validation on one code point at a time
138             {
139 14           auto last = in + size - 3;
140 20 100         while (in < last) if(!valid(in)) return false;
    100          
141             }
142              
143             tail:
144             // Handle the remaining bytes. The last
145             // characters could split a code point so
146             // we save the partial code point for later.
147             //
148             // On entry to the loop, `in` points to the
149             // beginning of a code point.
150             //
151 6           for (;;) {
152             // Number of chars left
153 18           size_t n = end - in;
154 18 100         if (!n) break;
155              
156             // Chars we need to finish this code point
157 10           auto const need = needed(*in);
158 10 50         if (need == 0) return false;
159 10 100         if (need <= n) {
160             // Check a whole code point
161 6 50         if (!valid(in)) return false;
162             }
163             else {
164             // Calculate how many chars we need
165             // to finish this partial code point
166 4           need_ = need - n;
167              
168             // Save the partial code point
169 8 100         while(n--) *p_++ = *in++;
170 4 50         assert(in == end);
171 4 50         assert(p_ <= cp_ + 4);
172              
173             // Do partial validation on the incomplete
174             // code point, this is called "Fail fast"
175             // in Autobahn|Testsuite parlance.
176 4           return !fail_fast();
177             }
178             }
179 14           return true;
180             }
181              
182             }}}