slow.monster

STUDIO HAZE · image

Topaz source

Open by house policy. Topaz is our own language.

Topaz 5.9 · 42 files

topaz/src/wire.tpz

Topaz · 15666 B · Original file

1// Canonical structure wire encoding and decoding.
3import std.bytes { Bytes }
4import std.json
5import util { 바이트로, 바이트배열로, 바이트추가, 부분배열 }
7export record 정수읽기 { 값: int, 다음: int }
8export record 문자열읽기 { 값: string, 다음: int }
9export record 사전읽기 { 값들: Array<string>, 다음: int }
10export record 코드단위문자열 {
11 키: string,
12 안전값: string,
13 안전: bool
15export record 코드단위문자열읽기 { 값: 코드단위문자열, 다음: int }
16export record 코드단위사전읽기 { 값들: Array<코드단위문자열>, 다음: int }
17export record 갈루아표 { 지수: Array<int>, 로그: Array<int> }
19// 구조 와이어의 낱말들. 십육진 표기와 가변 정수와 정렬 사전이
20// 위의 모든 계층이 함께 쓰는 어휘다.
21function 소문자십육진글자(값: int) -> string {
22 ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"][값]
25function 대문자십육진글자(값: int) -> string {
26 ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"][값]
29function 소문자십육진네자리(값: int) -> string {
30 소문자십육진글자((값 / 4096) % 16) + 소문자십육진글자((값 / 256) % 16) +
31 소문자십육진글자((값 / 16) % 16) + 소문자십육진글자(값 % 16)
34export function 대문자십육진네자리(값: int) -> string {
35 대문자십육진글자((값 / 4096) % 16) + 대문자십육진글자((값 / 256) % 16) +
36 대문자십육진글자((값 / 16) % 16) + 대문자십육진글자(값 % 16)
39export function 대문자십육진두자리(값: int) -> string {
40 대문자십육진글자((값 / 16) % 16) + 대문자십육진글자(값 % 16)
43function 십육진글자인가(글자: string) -> bool {
44 (글자 >= "0" && 글자 <= "9") || (글자 >= "a" && 글자 <= "f") ||
45 (글자 >= "A" && 글자 <= "F")
48function 소문자십육진글자인가(글자: string) -> bool {
49 (글자 >= "0" && 글자 <= "9") || (글자 >= "a" && 글자 <= "f")
52function 제이슨십육진숫자(글자: string) -> int {
53 if 글자 >= "0" && 글자 <= "9" { return (글자.codePointAt(0) ?? 48) - 48 }
54 if 글자 >= "a" && 글자 <= "f" { return (글자.codePointAt(0) ?? 97) - 97 + 10 }
55 (글자.codePointAt(0) ?? 65) - 65 + 10
58function 높은대리자인가(단위: int) -> bool { 단위 >= 55296 && 단위 <= 56319 }
59function 낮은대리자인가(단위: int) -> bool { 단위 >= 56320 && 단위 <= 57343 }
61export function 널문자있음(값: string) -> bool {
62 for 글자 in 값.scalars() {
63 if (글자.codePointAt(0) ?? -1) == 0 { return true }
64 }
65 false
68function 코드단위키만들기(단위들: Array<int>) -> string {
69 let mut 조각들: Array<string> = []
70 for 단위 in 단위들 { 조각들.push(소문자십육진네자리(단위)) }
71 조각들.join("")
74export function 안전문자열코드단위키(값: string) -> string {
75 let mut 조각들: Array<string> = []
76 for 글자 in 값.scalars() {
77 let 코드 = 글자.codePointAt(0) ?? 0
78 if 코드 <= 65535 { 조각들.push(소문자십육진네자리(코드)) }
79 else {
80 let 나머지 = 코드 - 65536
81 조각들.push(소문자십육진네자리(55296 + 나머지 / 1024))
82 조각들.push(소문자십육진네자리(56320 + 나머지 % 1024))
83 }
84 }
85 조각들.join("")
88export function 코드단위키읽기(키: string, 빈값허용: bool) -> Result<Array<int>, string> {
89 let 글자들 = 키.scalars()
90 if (!빈값허용 && 글자들.length == 0) || 글자들.length % 4 != 0 {
91 return Err("UTF-16 코드 단위 키 길이가 틀립니다")
92 }
93 let mut 단위들: Array<int> = []
94 let mut 색인 = 0
95 while 색인 < 글자들.length {
96 let mut 자리 = 0
97 let mut 값 = 0
98 while 자리 < 4 {
99 let 글자 = 글자들[색인 + 자리]
100 if !소문자십육진글자인가(글자) { return Err("UTF-16 코드 단위 키가 소문자 십육진수가 아닙니다") }
101 값 = 값 * 16 + 제이슨십육진숫자(글자)
102 자리 = 자리 + 1
103 }
104 단위들.push(값)
105 색인 = 색인 + 4
106 }
107 Ok(단위들)
110export function 짝없는대리자있음(단위들: Array<int>) -> bool {
111 let mut 색인 = 0
112 while 색인 < 단위들.length {
113 let 단위 = 단위들[색인]
114 if 높은대리자인가(단위) {
115 if 색인 + 1 < 단위들.length && 낮은대리자인가(단위들[색인 + 1]) { 색인 = 색인 + 2 }
116 else { return true }
117 } else if 낮은대리자인가(단위) { return true }
118 else { 색인 = 색인 + 1 }
119 }
120 false
123function 안전코드단위값(단위들: Array<int>) -> Result<string, string> {
124 if 짝없는대리자있음(단위들) { return Err("짝없는 UTF-16 대리자는 Topaz 문자열이 될 수 없습니다") }
125 let mut 조각들: Array<string> = []
126 let mut 색인 = 0
127 while 색인 < 단위들.length {
128 let 단위 = 단위들[색인]
129 let 코드 = if 높은대리자인가(단위) {
130 let 낮음 = 단위들[색인 + 1]
131 색인 = 색인 + 2
132 65536 + (단위 - 55296) * 1024 + (낮음 - 56320)
133 } else {
134 색인 = 색인 + 1
135 단위
136 }
137 match fromCodePoint(코드) {
138 case Some(글자) => { 조각들.push(글자) }
139 case None => return Err("UTF-16 코드 단위가 유니코드 스칼라가 아닙니다")
140 }
141 }
142 Ok(조각들.join(""))
145function 와이어제이슨문자열(키: string) -> Result<string, string> {
146 let 단위들 = 코드단위키읽기(키, true)?
147 let mut 조각들: Array<string> = ["\""]
148 let mut 색인 = 0
149 while 색인 < 단위들.length {
150 let 단위 = 단위들[색인]
151 if 단위 == 34 { 조각들.push("\\\"") }
152 else if 단위 == 92 { 조각들.push("\\\\") }
153 else if 단위 == 8 { 조각들.push("\\b") }
154 else if 단위 == 12 { 조각들.push("\\f") }
155 else if 단위 == 10 { 조각들.push("\\n") }
156 else if 단위 == 13 { 조각들.push("\\r") }
157 else if 단위 == 9 { 조각들.push("\\t") }
158 else if 높은대리자인가(단위) && 색인 + 1 < 단위들.length && 낮은대리자인가(단위들[색인 + 1]) {
159 let 코드 = 65536 + (단위 - 55296) * 1024 + (단위들[색인 + 1] - 56320)
160 match fromCodePoint(코드) {
161 case Some(문자) => { 조각들.push(문자) }
162 case None => return Err("UTF-16 대리자 쌍이 유니코드 스칼라가 아닙니다")
163 }
164 색인 = 색인 + 1
165 } else if 높은대리자인가(단위) || 낮은대리자인가(단위) || 단위 < 32 {
166 조각들.push("\\u")
167 조각들.push(소문자십육진네자리(단위))
168 } else {
169 match fromCodePoint(단위) {
170 case Some(문자) => { 조각들.push(문자) }
171 case None => return Err("UTF-16 코드 단위가 유니코드 스칼라가 아닙니다")
172 }
173 }
174 색인 = 색인 + 1
175 }
176 조각들.push("\"")
177 Ok(조각들.join(""))
180export function 코드단위문자열만들기(키: string) -> Result<코드단위문자열, string> {
181 let 단위들 = 코드단위키읽기(키, true)?
182 if 짝없는대리자있음(단위들) {
183 return Ok(코드단위문자열 { 키: 키, 안전값: "", 안전: false })
184 }
185 Ok(코드단위문자열 { 키: 키, 안전값: 안전코드단위값(단위들)?, 안전: true })
188function 제이슨문자열코드단위로(직렬: string) -> Result<코드단위문자열, string> {
189 let 글자들 = 직렬.scalars()
190 if 글자들.length < 2 || 글자들[0] != "\"" || 글자들[글자들.length - 1] != "\"" {
191 return Err("스칼라 문자열 사전 값이 JSON 문자열이 아닙니다")
192 }
193 let mut 단위들: Array<int> = []
194 let mut 색인 = 1
195 while 색인 < 글자들.length - 1 {
196 let 글자 = 글자들[색인]
197 if 글자 == "\"" { return Err("스칼라 문자열 사전에 닫는 따옴표 뒤 글자가 있습니다") }
198 if 글자 == "\\" {
199 if 색인 + 1 >= 글자들.length - 1 { return Err("스칼라 문자열 사전의 탈출 문자가 잘렸습니다") }
200 let 탈출 = 글자들[색인 + 1]
201 if 탈출 == "u" {
202 if 색인 + 5 >= 글자들.length - 1 { return Err("스칼라 문자열 사전의 유니코드 탈출이 잘렸습니다") }
203 let mut 자리 = 0
204 let mut 단위 = 0
205 while 자리 < 4 {
206 let 숫자글자 = 글자들[색인 + 2 + 자리]
207 if !십육진글자인가(숫자글자) { return Err("스칼라 문자열 사전의 유니코드 탈출이 틀립니다") }
208 단위 = 단위 * 16 + 제이슨십육진숫자(숫자글자)
209 자리 = 자리 + 1
210 }
211 단위들.push(단위)
212 색인 = 색인 + 6
213 } else {
214 if 탈출 == "\"" { 단위들.push(34) }
215 else if 탈출 == "\\" { 단위들.push(92) }
216 else if 탈출 == "/" { 단위들.push(47) }
217 else if 탈출 == "b" { 단위들.push(8) }
218 else if 탈출 == "f" { 단위들.push(12) }
219 else if 탈출 == "n" { 단위들.push(10) }
220 else if 탈출 == "r" { 단위들.push(13) }
221 else if 탈출 == "t" { 단위들.push(9) }
222 else { return Err("스칼라 문자열 사전의 탈출 문자가 틀립니다") }
223 색인 = 색인 + 2
224 }
225 } else {
226 let 코드 = 글자.codePointAt(0) ?? -1
227 if 코드 < 32 { return Err("스칼라 문자열 사전에 탈출하지 않은 제어 문자가 있습니다") }
228 if 코드 <= 65535 { 단위들.push(코드) }
229 else {
230 let 나머지 = 코드 - 65536
231 단위들.push(55296 + 나머지 / 1024)
232 단위들.push(56320 + 나머지 % 1024)
233 }
234 색인 = 색인 + 1
235 }
236 }
237 if 색인 != 글자들.length - 1 { return Err("스칼라 문자열 사전 값 길이가 틀립니다") }
238 let 키 = 코드단위키만들기(단위들)
239 if 와이어제이슨문자열(키)? != 직렬 { return Err("스칼라 문자열 사전 값이 정규 JSON이 아닙니다") }
240 if 짝없는대리자있음(단위들) {
241 Ok(코드단위문자열 { 키: 키, 안전값: "", 안전: false })
242 } else {
243 Ok(코드단위문자열 { 키: 키, 안전값: JSON.parseAs<string>(직렬)?, 안전: true })
244 }
247export function 사전색인(값들: Array<string>, 찾기: string) -> int {
248 let mut 색인 = 0
249 while 색인 < 값들.length {
250 if 값들[색인] == 찾기 { return 색인 }
251 색인 = 색인 + 1
252 }
253 -1
256export function 정렬고유(값들: Array<string>) -> Array<string> {
257 let 정렬 = 값들.sorted()
258 let mut 결과: Array<string> = []
259 for 값 in 정렬 {
260 if 결과.length == 0 || 결과[결과.length - 1] != 값 { 결과.push(값) }
261 }
262 결과
265export function 가변정수쓰기(출력: Array<int>, 원본: int) -> Array<int> {
266 let mut 결과 = 출력
267 let mut 남음 = 원본
268 while 남음 >= 128 {
269 결과.push((남음 % 128) + 128)
270 남음 = 남음 / 128
271 }
272 결과.push(남음)
273 결과
276export function 삼십이쓰기(출력: Array<int>, 값: int) -> Array<int> {
277 let mut 결과 = 출력
278 결과.push((값 / 16777216) % 256)
279 결과.push((값 / 65536) % 256)
280 결과.push((값 / 256) % 256)
281 결과.push(값 % 256)
282 결과
285export function 삼십이읽기(입력: Array<int>, 위치: int) -> int {
286 입력[위치] * 16777216 + 입력[위치 + 1] * 65536 + 입력[위치 + 2] * 256 + 입력[위치 + 3]
289function 정규문자열쓰기(출력: Array<int>, 값: string) -> Result<Array<int>, string> {
290 let 직렬 = JSON.stringify(값)?
291 let 바이트들 = 바이트배열로(Bytes.encodeUtf8(직렬))
292 let 길이쓴 = 가변정수쓰기(출력, 바이트들.length)
293 Ok(바이트추가(길이쓴, 바이트들))
296function 코드단위문자열쓰기(출력: Array<int>, 키: string) -> Result<Array<int>, string> {
297 let 직렬 = 와이어제이슨문자열(키)?
298 let 바이트들 = 바이트배열로(Bytes.encodeUtf8(직렬))
299 let 길이쓴 = 가변정수쓰기(출력, 바이트들.length)
300 Ok(바이트추가(길이쓴, 바이트들))
303export function 사전쓰기(출력: Array<int>, 값들: Array<string>) -> Result<Array<int>, string> {
304 let mut 결과 = 가변정수쓰기(출력, 값들.length)
305 for 값 in 값들 { 결과 = 정규문자열쓰기(결과, 값)? }
306 Ok(결과)
309export function 코드단위사전쓰기(출력: Array<int>, 키들: Array<string>) -> Result<Array<int>, string> {
310 let mut 결과 = 가변정수쓰기(출력, 키들.length)
311 for 키 in 키들 { 결과 = 코드단위문자열쓰기(결과, 키)? }
312 Ok(결과)
315export function 가변정수읽기(입력: Array<int>, 시작: int) -> Result<정수읽기, string> {
316 let mut 값 = 0
317 let mut 배수 = 1
318 let mut 위치 = 시작
319 let mut 색인 = 0
320 while 색인 < 8 {
321 if 위치 >= 입력.length { return Err("가변 정수가 잘렸습니다") }
322 let 바이트 = 입력[위치]
323 위치 = 위치 + 1
324 let 숫자 = 바이트 % 128
325 if 숫자 > (9007199254740991 - 값) / 배수 { return Err("가변 정수가 안전 범위를 넘습니다") }
326 값 = 값 + 숫자 * 배수
327 if 바이트 < 128 { return Ok(정수읽기 { 값: 값, 다음: 위치 }) }
328 배수 = 배수 * 128
329 색인 = 색인 + 1
330 }
331 Err("가변 정수가 너무 깁니다")
334function 정규문자열읽기(입력: Array<int>, 시작: int) -> Result<문자열읽기, string> {
335 let 길이읽기 = 가변정수읽기(입력, 시작)?
336 let 끝 = 길이읽기.다음 + 길이읽기.값
337 if 끝 > 입력.length { return Err("문자열이 잘렸습니다") }
338 let 조각 = 바이트로(부분배열(입력, 길이읽기.다음, 끝))?
339 let 직렬 = 조각.decodeUtf8()?
340 let 값 = JSON.parseAs<string>(직렬)?
341 if JSON.stringify(값)? != 직렬 { return Err("문자열이 정규 JSON이 아닙니다") }
342 Ok(문자열읽기 { 값: 값, 다음: 끝 })
345function 코드단위문자열해석(입력: Array<int>, 시작: int) -> Result<코드단위문자열읽기, string> {
346 let 길이읽기 = 가변정수읽기(입력, 시작)?
347 let 끝 = 길이읽기.다음 + 길이읽기.값
348 if 끝 > 입력.length { return Err("스칼라 문자열이 잘렸습니다") }
349 let 조각 = 바이트로(부분배열(입력, 길이읽기.다음, 끝))?
350 let 직렬 = 조각.decodeUtf8()?
351 Ok(코드단위문자열읽기 { 값: 제이슨문자열코드단위로(직렬)?, 다음: 끝 })
354export function 사전해석(입력: Array<int>, 시작: int) -> Result<사전읽기, string> {
355 let 수읽기 = 가변정수읽기(입력, 시작)?
356 let mut 위치 = 수읽기.다음
357 let mut 값들: Array<string> = []
358 let mut 색인 = 0
359 while 색인 < 수읽기.값 {
360 let 읽기 = 정규문자열읽기(입력, 위치)?
361 if 색인 > 0 && 값들[색인 - 1] >= 읽기.값 { return Err("사전이 엄격히 정렬되지 않았습니다") }
362 값들.push(읽기.값)
363 위치 = 읽기.다음
364 색인 = 색인 + 1
365 }
366 Ok(사전읽기 { 값들: 값들, 다음: 위치 })
369export function 코드단위사전해석(입력: Array<int>, 시작: int) -> Result<코드단위사전읽기, string> {
370 let 수읽기 = 가변정수읽기(입력, 시작)?
371 let mut 위치 = 수읽기.다음
372 let mut 값들: Array<코드단위문자열> = []
373 let mut 색인 = 0
374 while 색인 < 수읽기.값 {
375 let 읽기 = 코드단위문자열해석(입력, 위치)?
376 if 색인 > 0 && 값들[색인 - 1].키 >= 읽기.값.키 {
377 return Err("스칼라 문자열 사전이 UTF-16 코드 단위 순서로 정렬되지 않았습니다")
378 }
379 값들.push(읽기.값)
380 위치 = 읽기.다음
381 색인 = 색인 + 1
382 }
383 Ok(코드단위사전읽기 { 값들: 값들, 다음: 위치 })

SHA256 20a34b118b86712c60ba6fbe6a5f91b532bbc01d63fa1e0c421db2d77cca8e44