codeimage

STUDIO HAZE · image

Topaz source

Open by house policy. Topaz is our own language.

Topaz 5.9 · 42 files

topaz/src/core.tpz

Topaz · 746117 B · Original file

1// Public CodeImage ABI facade.
3import std.bytes { Bytes }
4import std.json
5import objc_xid { 오브젝티브씨식별자시작, 오브젝티브씨식별자계속 }
6import ecc { 갈루아곱, 다항식곱 }
7import hash { 해시배열 }
8import nodes { 노드종류 }
9import plan {
10 띠팔레트,
11 배열에있음,
12 범위씻김팔레트,
13 범위코드,
14 보호길이,
15 이미지계획,
16 이미지배치,
17 이미지배치계산,
18 이미지쪽계획,
19 이미지팔레트,
20 이미지프로필해시맞음,
21 이역할팔레트,
22 쪽투영문자열,
23 파일프로그램코드,
24 팔그물팔레트,
25 프로필해시,
26 픽셀글꼴,
27 형식팔레트,
29import print {
30 고닫는구분자,
31 고여는구분자,
32 글자합치기,
33 글평평하게,
34 끝가로공백제거,
35 끝공백제거,
36 댓글코드,
37 위치전진,
38 인쇄글조각,
39 인쇄조각같음,
40 인쇄조각이음,
41 인쇄조각자름,
42 인쇄조각접두,
43 인쇄조각접미,
44 인쇄조각판만들기,
45 인쇄조각평평하게,
46 인쇄조각행수,
47 인쇄호출,
48 제이슨문자필드,
49 제이슨정수필드,
50 줄과들여쓰기,
51 줄끝인가,
53import render {
54 글자그림,
55 사각안,
56 이미지머리,
57 이역할변형,
58 청크만들기,
60import schema {
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 하스켈원자본문코드,
94import types { 댓글고정, 분리본문키결과, 인쇄조각판 }
95import util {
96 거듭제곱이,
97 문자열배열에있음,
98 바이트로,
99 바이트배열로,
100 바이트추가,
101 배열같음,
102 부분배열,
103 비트배타합,
104 십육진숫자,
105 작은값,
106 큰값,
108import wire {
109 가변정수쓰기,
110 가변정수읽기,
111 갈루아표,
112 널문자있음,
113 대문자십육진네자리,
114 대문자십육진두자리,
115 사전색인,
116 사전쓰기,
117 사전읽기,
118 사전해석,
119 삼십이쓰기,
120 삼십이읽기,
121 안전문자열코드단위키,
122 정렬고유,
123 정수읽기,
124 짝없는대리자있음,
125 코드단위문자열,
126 코드단위문자열만들기,
127 코드단위사전쓰기,
128 코드단위사전읽기,
129 코드단위사전해석,
130 코드단위키읽기,
133export record 구조스칼라 {
134 필드: string,
135 태그: int,
136 정수값: int,
137 문자열값: string
140export record 구조노드 {
141 코드워드: int,
142 부모순번: int,
143 자식필드있음: bool,
144 자식필드: string,
145 자식색인: int,
146 시작: int,
147 끝: int,
148 스칼라들: Array<구조스칼라>,
149 식별자있음: bool,
150 식별자류번호: int
153export record 구조입력 {
154 분리본문키해시: string,
155 분리본문값수: int,
156 노드들: Array<구조노드>
159record 준비된구조 {
160 언어번호: int,
161 구조: 구조입력
164function 원본문자열리터럴(스칼라: 구조스칼라) -> Result<string, string> {
165 let 키 = if 스칼라.태그 == 5 { 안전문자열코드단위키(스칼라.문자열값) }
166 else if 스칼라.태그 == 6 { 스칼라.문자열값 }
167 else { return Err("문자열 리터럴 스칼라 태그가 틀립니다") }
168 let 단위들 = 코드단위키읽기(키, true)?
169 let mut 조각들: Array<string> = ["\""]
170 let mut 색인 = 0
171 while 색인 < 단위들.length {
172 let 단위 = 단위들[색인]
173 if 단위 == 34 { 조각들.push("\\\"") }
174 else if 단위 == 92 { 조각들.push("\\\\") }
175 else if 단위 == 8 { 조각들.push("\\b") }
176 else if 단위 == 9 { 조각들.push("\\t") }
177 else if 단위 == 10 { 조각들.push("\\n") }
178 else if 단위 == 12 { 조각들.push("\\f") }
179 else if 단위 == 13 { 조각들.push("\\r") }
180 else if 단위 == 0 {
181 let 다음숫자 = 색인 + 1 < 단위들.length && 단위들[색인 + 1] >= 48 && 단위들[색인 + 1] <= 57
182 조각들.push(if 다음숫자 { "\\x00" } else { "\\0" })
183 } else if 단위 >= 32 && 단위 <= 126 {
184 match fromCodePoint(단위) {
185 case Some(문자) => { 조각들.push(문자) }
186 case None => return Err("ASCII 코드 단위가 유니코드 스칼라가 아닙니다")
187 }
188 } else if 단위 <= 255 {
189 조각들.push("\\x")
190 조각들.push(대문자십육진두자리(단위))
191 } else {
192 조각들.push("\\u")
193 조각들.push(대문자십육진네자리(단위))
194 }
195 색인 = 색인 + 1
196 }
197 조각들.push("\"")
198 Ok(조각들.join(""))
201function 철자문자열리터럴(노드: 구조노드) -> Result<string, string> {
202 let 따옴표 = if 스칼라논리(노드, "spelling.singleQuote") { "'" } else { "\"" }
203 if 스칼라있음(노드, "spelling.escapeBody") {
204 return Ok(따옴표 + 스칼라문자(노드, "spelling.escapeBody") + 따옴표)
205 }
206 if 따옴표 == "'" { return Ok("'" + 스칼라문자(노드, "value") + "'") }
207 원본문자열리터럴(노드스칼라(노드, "value"))
210export function 해시이백오십육(입력: Bytes) -> Result<Bytes, string> {
211 바이트로(해시배열(바이트배열로(입력)))
214function 구조언어번호(구조: 구조입력) -> int {
215 if 구조.노드들.length == 0 { return 0 }
216 let 뿌리코드 = 구조.노드들[0].코드워드
217 if 노드종류(1, 뿌리코드) == 101 { return 1 }
218 if 노드종류(2, 뿌리코드) == 101 { return 2 }
219 if 노드종류(3, 뿌리코드) == 101 { return 3 }
220 if 노드종류(4, 뿌리코드) == 101 { return 4 }
221 if 노드종류(5, 뿌리코드) == 101 { return 5 }
222 if 노드종류(6, 뿌리코드) == 101 { return 6 }
223 if 노드종류(7, 뿌리코드) == 101 { return 7 }
224 if 노드종류(8, 뿌리코드) == 101 { return 8 }
225 if 노드종류(9, 뿌리코드) == 101 { return 9 }
226 if 노드종류(10, 뿌리코드) == 101 { return 10 }
227 if 노드종류(11, 뿌리코드) == 101 { return 11 }
228 if 노드종류(12, 뿌리코드) == 101 { return 12 }
229 if 노드종류(13, 뿌리코드) == 101 { return 13 }
230 if 노드종류(14, 뿌리코드) == 101 { return 14 }
231 if 노드종류(15, 뿌리코드) == 101 { return 15 }
232 if 노드종류(16, 뿌리코드) == 101 { return 16 }
233 if 노드종류(17, 뿌리코드) == 101 { return 17 }
234 if 노드종류(18, 뿌리코드) == 101 { return 18 }
235 if 노드종류(19, 뿌리코드) == 101 { return 19 }
236 if 노드종류(21, 뿌리코드) == 101 { return 21 }
237 if 노드종류(20, 뿌리코드) == 101 { return 20 }
238 if 노드종류(22, 뿌리코드) == 101 { return 22 }
239 if 노드종류(23, 뿌리코드) == 101 { return 23 }
240 if 노드종류(24, 뿌리코드) == 101 { return 24 }
241 if 노드종류(25, 뿌리코드) == 101 { return 25 }
242 if 노드종류(26, 뿌리코드) == 101 { return 26 }
243 if 노드종류(27, 뿌리코드) == 101 { return 27 }
244 if 노드종류(28, 뿌리코드) == 101 { return 28 }
245 if 노드종류(29, 뿌리코드) == 101 { return 29 }
246 if 노드종류(30, 뿌리코드) == 101 { return 30 }
247 0
250function 코드단위태그허용(언어번호: int, 노드: 구조노드, 스칼라: 구조스칼라) -> bool {
251 if 언어번호 != 1 && 언어번호 != 2 { return false }
252 let 종류 = 노드종류(언어번호, 노드.코드워드)
253 (종류 == 108 && 스칼라.필드 == "value") ||
254 (종류 == 116 && 스칼라.필드 == "value.cooked")
257function 구조스칼라검사(언어번호: int, 노드: 구조노드, 스칼라: 구조스칼라) -> Result<bool, string> {
258 if 스칼라.필드 == "" || 널문자있음(스칼라.필드) {
259 return Err("구조 스칼라 필드가 비었거나 영 문자를 포함합니다")
260 }
261 if 스칼라.필드 == "trailingFileLayout" {
262 if 언어번호 != 12 || 노드.부모순번 != -1 || 노드.코드워드 != 36 ||
263 스칼라.태그 != 5 || 스칼라.문자열값 == "" || 스칼라.문자열값.scalars().length > 4194304 {
264 return Err("Swift 파일 끝 배치가 정규형이 아닙니다")
265 }
266 for 글자 in 스칼라.문자열값.scalars() {
267 let 코드 = 글자.codePointAt(0) ?? 0
268 if 코드 != 32 && !(코드 >= 9 && 코드 <= 13) {
269 return Err("Swift 파일 끝 배치에 비배치 문자가 있습니다")
270 }
271 }
272 }
273 if 스칼라.태그 >= 0 && 스칼라.태그 <= 2 {
274 if 스칼라.정수값 != 0 || 스칼라.문자열값 != "" {
275 return Err("논리 구조 스칼라의 빈 본문이 정규형이 아닙니다")
276 }
277 return Ok(true)
278 }
279 if 스칼라.태그 == 3 {
280 if 스칼라.정수값 < 0 || 스칼라.정수값 > 9007199254740991 || 스칼라.문자열값 != "" {
281 return Err("양의 구조 스칼라가 안전 정수 정규형이 아닙니다")
282 }
283 return Ok(true)
284 }
285 if 스칼라.태그 == 4 {
286 if 스칼라.정수값 < 1 || 스칼라.정수값 > 9007199254740991 || 스칼라.문자열값 != "" {
287 return Err("음의 구조 스칼라가 안전 정수 정규형이 아닙니다")
288 }
289 return Ok(true)
290 }
291 if 스칼라.태그 == 5 {
292 if 스칼라.정수값 != 0 { return Err("문자열 구조 스칼라 정수 본문이 0이 아닙니다") }
293 return Ok(true)
294 }
295 if 스칼라.태그 == 6 {
296 if 스칼라.정수값 != 0 { return Err("UTF-16 호스트 스칼라 정수 본문이 0이 아닙니다") }
297 let 단위들 = 코드단위키읽기(스칼라.문자열값, false)?
298 if !짝없는대리자있음(단위들) {
299 return Err("UTF-16 호스트 스칼라에 짝없는 대리자가 없습니다")
300 }
301 if !코드단위태그허용(언어번호, 노드, 스칼라) {
302 return Err("UTF-16 호스트 스칼라가 허용되지 않은 구조 위치에 있습니다")
303 }
304 return Ok(true)
305 }
306 Err("지원하지 않는 구조 스칼라 태그입니다")
309function 구조입력검사(언어번호: int, 구조: 구조입력) -> Result<bool, string> {
310 for 노드 in 구조.노드들 {
311 let mut 첫필드 = true
312 let mut 이전필드 = ""
313 for 스칼라 in 노드.스칼라들 {
314 if !첫필드 && 이전필드 >= 스칼라.필드 { return Err("구조 스칼라 필드가 정규 순서가 아닙니다") }
315 구조스칼라검사(언어번호, 노드, 스칼라)?
316 이전필드 = 스칼라.필드
317 첫필드 = false
318 }
319 }
320 Ok(true)
323function 구조스칼라코드단위문자열(스칼라: 구조스칼라) -> Result<코드단위문자열, string> {
324 if 스칼라.태그 == 5 {
325 let 키 = 안전문자열코드단위키(스칼라.문자열값)
326 return Ok(코드단위문자열 { 키: 키, 안전값: 스칼라.문자열값, 안전: true })
327 }
328 if 스칼라.태그 == 6 { return 코드단위문자열만들기(스칼라.문자열값) }
329 Err("문자열이 아닌 구조 스칼라입니다")
332record 압축원본행 {
333 행: int,
334 시작: int
337function 원본기하필드인가(필드: string) -> bool {
338 필드 == "startRow" || 필드 == "startColumn" ||
339 필드 == "endRow" || 필드 == "endColumn"
342function 압축원본행들(구조: 구조입력) -> Result<Array<압축원본행>, string> {
343 let mut 행들: Array<압축원본행> = []
344 for 노드 in 구조.노드들 {
345 let mut 모서리 = 0
346 while 모서리 < 2 {
347 let 행필드 = if 모서리 == 0 { "startRow" } else { "endRow" }
348 let 열필드 = if 모서리 == 0 { "startColumn" } else { "endColumn" }
349 let 오프셋 = if 모서리 == 0 { 노드.시작 } else { 노드.끝 }
350 let 행스칼라 = 노드스칼라(노드, 행필드)
351 let 열스칼라 = 노드스칼라(노드, 열필드)
352 if 행스칼라.태그 != 3 || 열스칼라.태그 != 3 ||
353 행스칼라.정수값 < 0 || 열스칼라.정수값 < 0 || 열스칼라.정수값 > 오프셋 {
354 return Err("JS/TS 원본 행 기하가 틀립니다")
355 }
356 let 행 = 행스칼라.정수값
357 let 행시작 = 오프셋 - 열스칼라.정수값
358 let mut 찾음 = false
359 for 기존 in 행들 {
360 if 기존.행 == 행 {
361 if 기존.시작 != 행시작 { return Err("한 원본 행의 시작 오프셋이 서로 다릅니다") }
362 찾음 = true
363 }
364 }
365 if !찾음 { 행들.push(압축원본행 { 행: 행, 시작: 행시작 }) }
366 모서리 = 모서리 + 1
367 }
368 }
369 let mut 왼쪽 = 0
370 while 왼쪽 < 행들.length {
371 let mut 오른쪽 = 왼쪽 + 1
372 while 오른쪽 < 행들.length {
373 if 행들[오른쪽].행 < 행들[왼쪽].행 {
374 let 임시 = 행들[왼쪽]
375 행들[왼쪽] = 행들[오른쪽]
376 행들[오른쪽] = 임시
377 }
378 오른쪽 = 오른쪽 + 1
379 }
380 왼쪽 = 왼쪽 + 1
381 }
382 let mut 색인 = 1
383 while 색인 < 행들.length {
384 if 행들[색인].시작 <= 행들[색인 - 1].시작 {
385 return Err("원본 행 시작 오프셋이 엄격히 증가하지 않습니다")
386 }
387 색인 = 색인 + 1
388 }
389 Ok(행들)
392function 압축원본문자위치(
393 행들: Array<압축원본행>,
394 오프셋: int
395) -> Result<Array<int>, string> {
396 let mut 아래 = -1
397 let mut 위 = 행들.length
398 while 아래 + 1 < 위 {
399 let 가운데 = (아래 + 위) / 2
400 if 행들[가운데].시작 <= 오프셋 { 아래 = 가운데 } else { 위 = 가운데 }
401 }
402 if 아래 < 0 { return Err("노드 오프셋이 첫 원본 행보다 앞섭니다") }
403 Ok([행들[아래].행, 오프셋 - 행들[아래].시작])
406function 구조스칼라필드정렬(값들: Array<구조스칼라>) -> Array<구조스칼라> {
407 let mut 결과 = 값들
408 let mut 왼쪽 = 0
409 while 왼쪽 < 결과.length {
410 let mut 오른쪽 = 왼쪽 + 1
411 while 오른쪽 < 결과.length {
412 if 결과[오른쪽].필드 < 결과[왼쪽].필드 {
413 let 임시 = 결과[왼쪽]
414 결과[왼쪽] = 결과[오른쪽]
415 결과[오른쪽] = 임시
416 }
417 오른쪽 = 오른쪽 + 1
418 }
419 왼쪽 = 왼쪽 + 1
420 }
421 결과
424function 구조인코드배열(구조: 구조입력) -> Result<Array<int>, string> {
425 let 언어번호 = 구조언어번호(구조)
426 let 압축기하 = 언어번호 == 1 || 언어번호 == 2
427 구조입력검사(언어번호, 구조)?
428 if 구조.분리본문키해시.scalars().length != 64 { return Err("분리 본문 키 해시 길이가 틀립니다") }
429 let mut 자식모음: Array<string> = []
430 let mut 스칼라필드모음: Array<string> = []
431 let mut 스칼라문자키들: Array<string> = []
432 for 노드 in 구조.노드들 {
433 if 노드.자식필드있음 { 자식모음.push(노드.자식필드) }
434 for 스칼라 in 노드.스칼라들 {
435 if !압축기하 || !원본기하필드인가(스칼라.필드) {
436 스칼라필드모음.push(스칼라.필드)
437 if 스칼라.태그 == 5 || 스칼라.태그 == 6 {
438 let 코드단위문자열 = 구조스칼라코드단위문자열(스칼라)?
439 스칼라문자키들.push(코드단위문자열.키)
440 }
441 }
442 }
443 }
444 let mut 원본행들: Array<압축원본행> = []
445 if 압축기하 { 원본행들 = 압축원본행들(구조)? }
446 let 자식사전 = 정렬고유(자식모음)
447 let 스칼라필드사전 = 정렬고유(스칼라필드모음)
448 let 스칼라문자사전 = 정렬고유(스칼라문자키들)
449 let mut 내용 = 바이트배열로(Bytes.fromHex(구조.분리본문키해시)?)
450 내용 = 가변정수쓰기(내용, 구조.노드들.length)
451 내용 = 가변정수쓰기(내용, 구조.분리본문값수)
452 if 압축기하 {
453 내용 = 가변정수쓰기(내용, 원본행들.length)
454 let mut 이전행 = -1
455 let mut 이전시작 = 0
456 for 행 in 원본행들 {
457 내용 = 가변정수쓰기(내용, 행.행 - 이전행)
458 내용 = 가변정수쓰기(내용, 행.시작 - 이전시작)
459 이전행 = 행.행
460 이전시작 = 행.시작
461 }
462 }
463 내용 = 사전쓰기(내용, 자식사전)?
464 내용 = 사전쓰기(내용, 스칼라필드사전)?
465 내용 = 코드단위사전쓰기(내용, 스칼라문자사전)?
466 let mut 순번 = 0
467 let mut 스칼라문자순번 = 0
468 for 노드 in 구조.노드들 {
469 내용 = 가변정수쓰기(내용, 노드.코드워드)
470 내용 = 가변정수쓰기(내용, if 노드.부모순번 < 0 { 0 } else { 순번 - 노드.부모순번 })
471 내용 = 가변정수쓰기(내용, if 노드.자식필드있음 { 사전색인(자식사전, 노드.자식필드) + 1 } else { 0 })
472 내용 = 가변정수쓰기(내용, if 노드.자식색인 < 0 { 0 } else { 노드.자식색인 + 1 })
473 내용 = 가변정수쓰기(내용, 노드.시작)
474 내용 = 가변정수쓰기(내용, 노드.끝)
475 let mut 스칼라수 = 0
476 for 스칼라 in 노드.스칼라들 {
477 if !압축기하 || !원본기하필드인가(스칼라.필드) { 스칼라수 = 스칼라수 + 1 }
478 }
479 내용 = 가변정수쓰기(내용, 스칼라수)
480 for 스칼라 in 노드.스칼라들 {
481 if !압축기하 || !원본기하필드인가(스칼라.필드) {
482 내용 = 가변정수쓰기(내용, 사전색인(스칼라필드사전, 스칼라.필드))
483 내용.push(if 스칼라.태그 == 6 { 5 } else { 스칼라.태그 })
484 if 스칼라.태그 == 3 || 스칼라.태그 == 4 { 내용 = 가변정수쓰기(내용, 스칼라.정수값) }
485 else if 스칼라.태그 == 5 || 스칼라.태그 == 6 {
486 if 스칼라문자순번 >= 스칼라문자키들.length {
487 return Err("스칼라 문자열 키 캐시가 구조보다 짧습니다")
488 }
489 내용 = 가변정수쓰기(
490 내용,
491 사전색인(스칼라문자사전, 스칼라문자키들[스칼라문자순번])
492 )
493 스칼라문자순번 = 스칼라문자순번 + 1
494 }
495 }
496 }
497 if 노드.식별자있음 { 내용 = 가변정수쓰기(내용, 노드.식별자류번호) }
498 순번 = 순번 + 1
499 }
500 if 스칼라문자순번 != 스칼라문자키들.length {
501 return Err("스칼라 문자열 키 캐시가 구조보다 깁니다")
502 }
503 let 구조버전 = if 언어번호 == 1 || 언어번호 == 2 { 4 } else { 1 }
504 let mut 본문 = [67, 73, 70, 83, 구조버전, 1, 0, 0]
505 본문 = 삼십이쓰기(본문, 16 + 내용.length + 32)
506 본문 = 삼십이쓰기(본문, 내용.length)
507 본문 = 바이트추가(본문, 내용)
508 let 결과 = 바이트추가(본문, 해시배열(본문))
509 Ok(결과)
512export function 구조인코드(입력: string) -> Result<Bytes, string> {
513 let 구조 = JSON.parseAs<구조입력>(입력)?
514 바이트로(구조인코드배열(구조)?)
517export function 구조디코드(와이어: Bytes, 식별자표: Array<bool>) -> Result<string, string> {
518 let 전체 = 바이트배열로(와이어)
519 if 전체.length < 48 { return Err("구조 와이어가 잘렸습니다") }
520 if 전체.length > 2097152 { return Err("구조 와이어가 허용량을 넘습니다") }
521 let 본문 = 부분배열(전체, 0, 전체.length - 32)
522 let 실제해시 = 부분배열(전체, 전체.length - 32, 전체.length)
523 if !배열같음(실제해시, 해시배열(본문)) { return Err("구조 와이어 검사합이 맞지 않습니다") }
524 if 본문[0] != 67 || 본문[1] != 73 || 본문[2] != 70 || 본문[3] != 83 { return Err("구조 와이어 마법값이 틀립니다") }
525 if (본문[4] != 1 && 본문[4] != 4) || 본문[5] != 1 || 본문[6] != 0 || 본문[7] != 0 {
526 return Err("구조 와이어 버전이 틀립니다")
527 }
528 let 와이어버전 = 본문[4]
529 if 삼십이읽기(본문, 8) != 전체.length || 삼십이읽기(본문, 12) != 본문.length - 16 { return Err("구조 와이어 길이가 틀립니다") }
530 let mut 위치 = 16
531 let 해시끝 = 위치 + 32
532 let 분리본문키바이트 = 바이트로(부분배열(본문, 위치, 해시끝))?
533 let 분리본문키해시 = 분리본문키바이트.toHex()
534 위치 = 해시끝
535 let 노드수읽기 = 가변정수읽기(본문, 위치)?; 위치 = 노드수읽기.다음
536 let 본문수읽기 = 가변정수읽기(본문, 위치)?; 위치 = 본문수읽기.다음
537 if 식별자표.length != 노드수읽기.값 { return Err("식별자 표 길이가 노드 수와 다릅니다") }
538 let mut 원본행들: Array<압축원본행> = []
539 if 와이어버전 == 4 {
540 let 행수읽기 = 가변정수읽기(본문, 위치)?; 위치 = 행수읽기.다음
541 if 행수읽기.값 == 0 || 행수읽기.값 > 노드수읽기.값 * 2 {
542 return Err("원본 행 수가 허용량을 넘습니다")
543 }
544 let mut 이전행 = -1
545 let mut 이전시작 = 0
546 let mut 행색인 = 0
547 while 행색인 < 행수읽기.값 {
548 let 행차 = 가변정수읽기(본문, 위치)?; 위치 = 행차.다음
549 let 시작차 = 가변정수읽기(본문, 위치)?; 위치 = 시작차.다음
550 if 행차.값 == 0 || (행색인 > 0 && 시작차.값 == 0) {
551 return Err("원본 행들이 엄격히 증가하지 않습니다")
552 }
553 let 행 = 이전행 + 행차.값
554 let 시작 = 이전시작 + 시작차.값
555 원본행들.push(압축원본행 { 행: 행, 시작: 시작 })
556 이전행 = 행
557 이전시작 = 시작
558 행색인 = 행색인 + 1
559 }
560 }
561 let 자식읽기 = 사전해석(본문, 위치)?; 위치 = 자식읽기.다음
562 let 필드읽기 = 사전해석(본문, 위치)?; 위치 = 필드읽기.다음
563 if 와이어버전 == 4 {
564 for 필드 in 필드읽기.값들 {
565 if 원본기하필드인가(필드) { return Err("원본 기하가 스칼라 사전에 중복되었습니다") }
566 }
567 }
568 let 문자읽기 = 코드단위사전해석(본문, 위치)?; 위치 = 문자읽기.다음
569 let mut 노드들: Array<구조노드> = []
570 let mut 순번 = 0
571 while 순번 < 노드수읽기.값 {
572 let 코드 = 가변정수읽기(본문, 위치)?; 위치 = 코드.다음
573 let 부모차 = 가변정수읽기(본문, 위치)?; 위치 = 부모차.다음
574 let 자식필드코드 = 가변정수읽기(본문, 위치)?; 위치 = 자식필드코드.다음
575 let 자식색인코드 = 가변정수읽기(본문, 위치)?; 위치 = 자식색인코드.다음
576 let 시작읽기 = 가변정수읽기(본문, 위치)?; 위치 = 시작읽기.다음
577 let 끝읽기 = 가변정수읽기(본문, 위치)?; 위치 = 끝읽기.다음
578 let 스칼라수 = 가변정수읽기(본문, 위치)?; 위치 = 스칼라수.다음
579 if 자식필드코드.값 > 자식읽기.값들.length { return Err("자식 필드 사전 색인이 틀립니다") }
580 let mut 스칼라들: Array<구조스칼라> = []
581 let mut 스칼라색인 = 0
582 let mut 이전필드 = -1
583 while 스칼라색인 < 스칼라수.값 {
584 let 필드색인 = 가변정수읽기(본문, 위치)?; 위치 = 필드색인.다음
585 if 필드색인.값 >= 필드읽기.값들.length || 필드색인.값 <= 이전필드 { return Err("스칼라 필드가 정규 순서가 아닙니다") }
586 이전필드 = 필드색인.값
587 if 위치 >= 본문.length { return Err("스칼라 태그가 잘렸습니다") }
588 let 태그 = 본문[위치]; 위치 = 위치 + 1
589 let mut 호스트태그 = 태그
590 let mut 정수값 = 0
591 let mut 문자열값 = ""
592 if 태그 == 3 || 태그 == 4 {
593 let 정수읽기값 = 가변정수읽기(본문, 위치)?; 위치 = 정수읽기값.다음
594 정수값 = 정수읽기값.값
595 if 태그 == 4 && 정수값 == 0 { return Err("음수 영은 허용되지 않습니다") }
596 } else if 태그 == 5 {
597 let 문자열색인 = 가변정수읽기(본문, 위치)?; 위치 = 문자열색인.다음
598 if 문자열색인.값 >= 문자읽기.값들.length { return Err("스칼라 문자열 사전 색인이 틀립니다") }
599 let 문자값 = 문자읽기.값들[문자열색인.값]
600 if 문자값.안전 { 문자열값 = 문자값.안전값 }
601 else { 호스트태그 = 6; 문자열값 = 문자값.키 }
602 } else if 태그 < 0 || 태그 > 2 { return Err("스칼라 태그가 틀립니다") }
603 스칼라들.push(구조스칼라 {
604 필드: 필드읽기.값들[필드색인.값], 태그: 호스트태그,
605 정수값: 정수값, 문자열값: 문자열값
606 })
607 스칼라색인 = 스칼라색인 + 1
608 }
609 if 와이어버전 == 4 {
610 let 시작기하 = 압축원본문자위치(원본행들, 시작읽기.값)?
611 let 끝기하 = 압축원본문자위치(원본행들, 끝읽기.값)?
612 스칼라들.push(구조스칼라 { 필드: "startRow", 태그: 3, 정수값: 시작기하[0], 문자열값: "" })
613 스칼라들.push(구조스칼라 { 필드: "startColumn", 태그: 3, 정수값: 시작기하[1], 문자열값: "" })
614 스칼라들.push(구조스칼라 { 필드: "endRow", 태그: 3, 정수값: 끝기하[0], 문자열값: "" })
615 스칼라들.push(구조스칼라 { 필드: "endColumn", 태그: 3, 정수값: 끝기하[1], 문자열값: "" })
616 스칼라들 = 구조스칼라필드정렬(스칼라들)
617 }
618 let 식별자있음 = 식별자표[순번]
619 let mut 식별자류번호 = 0
620 if 식별자있음 {
621 let 식별자읽기 = 가변정수읽기(본문, 위치)?; 위치 = 식별자읽기.다음
622 식별자류번호 = 식별자읽기.값
623 }
624 노드들.push(구조노드 {
625 코드워드: 코드.값,
626 부모순번: if 순번 == 0 { -1 } else { 순번 - 부모차.값 },
627 자식필드있음: 자식필드코드.값 != 0,
628 자식필드: if 자식필드코드.값 == 0 { "" } else { 자식읽기.값들[자식필드코드.값 - 1] },
629 자식색인: if 자식색인코드.값 == 0 { -1 } else { 자식색인코드.값 - 1 },
630 시작: 시작읽기.값,
631 끝: 끝읽기.값,
632 스칼라들: 스칼라들,
633 식별자있음: 식별자있음,
634 식별자류번호: 식별자류번호
635 })
636 순번 = 순번 + 1
637 }
638 if 위치 != 본문.length { return Err("구조 와이어 뒤에 바이트가 남았습니다") }
639 let 구조 = 구조입력 { 분리본문키해시: 분리본문키해시, 분리본문값수: 본문수읽기.값, 노드들: 노드들 }
640 let 언어번호 = 구조언어번호(구조)
641 let 예상버전 = if 언어번호 == 1 || 언어번호 == 2 { 4 } else { 1 }
642 if 와이어버전 != 예상버전 { return Err("구조 와이어 버전이 언어와 맞지 않습니다") }
643 구조입력검사(언어번호, 구조)?
644 if !배열같음(구조인코드배열(구조)?, 전체) { return Err("구조 와이어가 정규 인코딩이 아닙니다") }
645 JSON.stringify(구조)
648let 체다항식 = 285
649let 부호바이트수 = 255
650let 자료바이트수 = 223
651let 패리티바이트수 = 32
653// 갈루아 체 GF(256)의 곱셈표. 상수를 외워 적지 않고 원시원소 2를
654// 거듭제곱하며 지수표를 기른다. 값이 넘칠 때마다 체다항식으로 줄이면
655// 255걸음 만에 0을 제외한 모든 원소를 한 번씩 지난다. 지수표를 두 배
656// 길이로 이어 두면 로그 두 개의 합이 255를 넘어도 나머지 연산 없이
657// 바로 읽을 수 있다.
658function 갈루아표만들기() -> 갈루아표 {
659 let mut 지수: Array<int> = []
660 let mut 로그: Array<int> = []
661 let mut 색인 = 0
662 while 색인 < 512 { 지수.push(0); 색인 = 색인 + 1 }
663 색인 = 0
664 while 색인 < 256 { 로그.push(0); 색인 = 색인 + 1 }
665 let mut 값 = 1
666 색인 = 0
667 while 색인 < 255 {
668 지수[색인] = 값
669 로그[값] = 색인
670 값 = 값 * 2
671 if 값 >= 256 { 값 = 비트배타합(값, 체다항식, 9) }
672 색인 = 색인 + 1
673 }
674 while 색인 < 512 { 지수[색인] = 지수[색인 - 255]; 색인 = 색인 + 1 }
675 갈루아표 { 지수: 지수, 로그: 로그 }
678// 생성다항식은 일차식 (x - α^i)를 근 α^0부터 α^31까지 차례로 곱해
679// 자라난다. 서른두 개의 근이 서른두 바이트의 패리티를 만든다.
680function 생성다항식(표: 갈루아표) -> Array<int> {
681 let mut 생성 = [1]
682 let mut 근 = 0
683 while 근 < 패리티바이트수 { 생성 = 다항식곱(생성, [1, 표.지수[근]], 표); 근 = 근 + 1 }
684 생성
687// 패리티는 다항식 나눗셈의 나머지다. 자료를 255칸 작업대의 앞 223칸에
688// 펼치고 위치마다 생성다항식 배수를 빼 나가면 앞쪽은 전부 지워지고
689// 마지막 서른두 칸에 나머지만 남는다.
690function 패리티(자료: Array<int>, 표: 갈루아표, 생성: Array<int>) -> Result<Array<int>, string> {
691 if 자료.length != 자료바이트수 { return Err("리드 솔로몬 자료 조각은 223바이트여야 합니다") }
692 let mut 작업: Array<int> = []
693 let mut 색인 = 0
694 while 색인 < 부호바이트수 { 작업.push(if 색인 < 자료바이트수 { 자료[색인] } else { 0 }); 색인 = 색인 + 1 }
695 let mut 위치 = 0
696 while 위치 < 자료바이트수 {
697 let 계수 = 작업[위치]
698 if 계수 != 0 {
699 색인 = 0
700 while 색인 < 생성.length {
701 작업[위치 + 색인] = 비트배타합(작업[위치 + 색인], 갈루아곱(생성[색인], 계수, 표), 8)
702 색인 = 색인 + 1
703 }
704 }
705 위치 = 위치 + 1
706 }
707 Ok(부분배열(작업, 자료바이트수, 부호바이트수))
710// 보호 방향은 승인된 기성 부호기가 맡는다. 해제 방향은 아래에서 같은
711// 수학을 열린 토파즈로 다시 유도해 검증하므로 읽는 사람이 두 방향의
712// 일치를 직접 확인할 수 있다.
713export function 리드솔로몬보호(논리: Bytes) -> Result<Bytes, string> {
714 if 논리.length() == 0 { return Err("논리 와이어는 비어 있을 수 없습니다") }
715 let 조각수 = (논리.length() + 자료바이트수 - 1) / 자료바이트수
716 if 조각수 > 65535 { return Err("리드 솔로몬 조각 수가 너무 많습니다") }
717 Codec.reedSolomon255223Protect(논리)
720export function 리드솔로몬해제(보호: Bytes, 논리길이: int) -> Result<Bytes, string> {
721 let 입력 = 바이트배열로(보호)
722 if 입력.length == 0 || 입력.length % 부호바이트수 != 0 { return Err("보호 와이어가 조각 경계에 맞지 않습니다") }
723 let 조각수 = 입력.length / 부호바이트수
724 let 최대길이 = 조각수 * 자료바이트수
725 if 논리길이 <= 0 || 논리길이 > 최대길이 || 논리길이 <= 최대길이 - 자료바이트수 { return Err("논리 길이가 정규 범위가 아닙니다") }
726 let 표 = 갈루아표만들기()
727 let 생성 = 생성다항식(표)
728 let mut 자료전체: Array<int> = []
729 let mut 조각 = 0
730 while 조각 < 조각수 {
731 let 시작 = 조각 * 부호바이트수
732 let 자료 = 부분배열(입력, 시작, 시작 + 자료바이트수)
733 let 실제 = 부분배열(입력, 시작 + 자료바이트수, 시작 + 부호바이트수)
734 if !배열같음(패리티(자료, 표, 생성)?, 실제) { return Err("리드 솔로몬 패리티가 맞지 않습니다") }
735 자료전체 = 바이트추가(자료전체, 자료)
736 조각 = 조각 + 1
737 }
738 let mut 색인 = 논리길이
739 while 색인 < 자료전체.length {
740 if 자료전체[색인] != 0 { return Err("리드 솔로몬 채움 바이트가 영이 아닙니다") }
741 색인 = 색인 + 1
742 }
743 바이트로(부분배열(자료전체, 0, 논리길이))
746// 이미지 운반체. 큰 래스터는 바이트 배열로 펼치지 않고 좌표 함수로
747// 계산하여 불변 바이트 경계를 지킨다.
749record 이미지요청 {
750 언어번호: int,
751 팔레트번호: int,
752 프로필해시: string,
753 구조해시: string,
754 키해시: string,
755 크롬번호: int,
756 제목: string,
757 원본줄수: int,
758 구조: 구조입력
761record 준비된이미지요청 {
762 요청: 이미지요청,
763 구조: 준비된구조,
764 논리: Bytes
767record 준비된이미지계획 {
768 요청: 준비된이미지요청,
769 계획: 이미지계획
772export record 이미지계획출력 {
773 쪽수: int,
774 세트해시: string,
775 높이들: Array<int>,
776 노드시작들: Array<int>,
777 노드수들: Array<int>,
778 논리길이들: Array<int>,
779 보호길이들: Array<int>,
780 패리티길이들: Array<int>
783record 그림준비 {
784 요청: 이미지요청,
785 머리: 이미지머리,
786 머리바이트: ByteBuffer,
787 보호바이트: ByteBuffer,
788 노드시작: int,
789 노드끝: int,
790 수직씻김: Array<int>,
791 수평씻김: Array<int>,
792 이역할: Array<int>,
793 팔그물: Array<int>
795record 주사그림 { 준비: 그림준비, 바이트들: ByteBuffer }
797function 요청검사(요청: 이미지요청) -> Result<준비된구조, string> {
798 if 요청.언어번호 != 1 && 요청.언어번호 != 2 && 요청.언어번호 != 3 &&
799 요청.언어번호 != 4 && 요청.언어번호 != 5 &&
800 요청.언어번호 != 6 && 요청.언어번호 != 7 &&
801 요청.언어번호 != 8 && 요청.언어번호 != 9 &&
802 요청.언어번호 != 10 && 요청.언어번호 != 11 &&
803 요청.언어번호 != 12 && 요청.언어번호 != 13 &&
804 요청.언어번호 != 14 && 요청.언어번호 != 15 &&
805 요청.언어번호 != 16 && 요청.언어번호 != 17 &&
806 요청.언어번호 != 18 && 요청.언어번호 != 19 &&
807 요청.언어번호 != 21 && 요청.언어번호 != 20 &&
808 요청.언어번호 != 22 && 요청.언어번호 != 23 &&
809 요청.언어번호 != 24 && 요청.언어번호 != 25 &&
810 요청.언어번호 != 26 && 요청.언어번호 != 27 &&
811 요청.언어번호 != 28 && 요청.언어번호 != 29 &&
812 요청.언어번호 != 30 {
813 return Err("지원하지 않는 언어입니다")
814 }
815 if 구조언어번호(요청.구조) != 요청.언어번호 { return Err("구조 뿌리의 언어가 이미지 요청과 다릅니다") }
816 인쇄구조검사(요청.언어번호, 요청.구조)?
817 if 요청.팔레트번호 != 요청.언어번호 || !이미지프로필해시맞음(요청.언어번호, 요청.프로필해시) {
818 return Err("언어 팔레트 프로필이 맞지 않습니다")
819 }
820 if 요청.프로필해시.scalars().length != 64 || 요청.구조해시.scalars().length != 64 || 요청.키해시.scalars().length != 64 {
821 return Err("이미지 해시 길이가 틀립니다")
822 }
823 if 요청.구조.분리본문키해시 != 요청.키해시 { return Err("구조와 키 해시가 다릅니다") }
824 if 요청.구조.노드들.length == 0 { return Err("이미지는 파일 뿌리 노드가 필요합니다") }
825 if 요청.크롬번호 < 0 || 요청.크롬번호 > 1 { return Err("크롬 번호가 틀립니다") }
826 if 요청.크롬번호 == 0 && (요청.제목 != "" || 요청.원본줄수 != 0) { return Err("민무늬 크롬에 책 메타데이터가 있습니다") }
827 let 제목바이트 = Bytes.encodeUtf8(요청.제목)
828 if 제목바이트.length() > 24 { return Err("책 제목이 24바이트를 넘습니다") }
829 for 글자 in 요청.제목.scalars() {
830 if 픽셀글꼴(글자) == "" { return Err("책 제목에 없는 픽셀 글자가 있습니다") }
831 }
832 Ok(준비된구조 { 언어번호: 요청.언어번호, 구조: 요청.구조 })
835function 이미지요청준비(요청글: string, 논리: Bytes) -> Result<준비된이미지요청, string> {
836 let 요청 = JSON.parseAs<이미지요청>(요청글)?
837 let 준비된구조값 = 요청검사(요청)?
838 if 논리.length() <= 0 { return Err("구조 논리 와이어가 비었습니다") }
839 Ok(준비된이미지요청 {
840 요청: 요청,
841 구조: 준비된구조값,
842 논리: 논리
843 })
846function 조상또는자신(노드들: Array<구조노드>, 순번: int, 조상: int) -> bool {
847 let mut 현재 = 순번
848 while 현재 >= 0 {
849 if 현재 == 조상 { return true }
850 현재 = 노드들[현재].부모순번
851 }
852 false
855function 범위깊이(노드들: Array<구조노드>, 언어번호: int, 순번: int) -> int {
856 let mut 깊이 = 0
857 let mut 현재 = 순번
858 while 현재 >= 0 {
859 let 노드 = 노드들[현재]
860 if 범위코드(언어번호, 노드.코드워드) && !파일프로그램코드(언어번호, 노드.코드워드) { 깊이 = 깊이 + 1 }
861 현재 = 노드.부모순번
862 }
863 깊이
866function 선호행경계(요청: 이미지요청) -> Array<int> {
867 let 노드들 = 요청.구조.노드들
868 let mut 경계들: Array<int> = []
869 let mut 순번 = 0
870 while 순번 < 노드들.length {
871 let 노드 = 노드들[순번]
872 if 범위코드(요청.언어번호, 노드.코드워드) && !파일프로그램코드(요청.언어번호, 노드.코드워드) {
873 let mut 끝 = 순번
874 let mut 살피기 = 순번 + 1
875 while 살피기 < 노드들.length && 조상또는자신(노드들, 살피기, 순번) { 끝 = 살피기; 살피기 = 살피기 + 1 }
876 if (끝 + 1) % 12 == 0 { 경계들.push((끝 + 1) / 12) }
877 }
878 순번 = 순번 + 1
879 }
880 경계들
883function 이미지세트해시계산(요청: 이미지요청, 논리: Bytes, 쪽들: Array<이미지쪽계획>) -> Result<string, string> {
884 let 크롬글 = if 요청.크롬번호 == 1 { "book" } else { "plain" }
885 let 논리해시바이트 = 해시이백오십육(논리)?
886 let 논리해시 = 논리해시바이트.toHex()
887 let 투영 = "\{" + JSON.stringify("chromePreset")? + ":" + JSON.stringify(크롬글)? +
888 "," + JSON.stringify("detachedTextKeySha256")? + ":" + JSON.stringify("sha256:" + 요청.키해시)? +
889 "," + JSON.stringify("domain")? + ":" + JSON.stringify("codeimage.js-total-format.v2.pageset")? +
890 "," + JSON.stringify("pages")? + ":" + 쪽투영문자열(쪽들)? +
891 "," + JSON.stringify("sourceLineCount")? + ":" + "{요청.원본줄수}" +
892 "," + JSON.stringify("structureDigest")? + ":" + JSON.stringify("sha256:" + 요청.구조해시)? +
893 "," + JSON.stringify("structureLogicalSha256")? + ":" + JSON.stringify("sha256:" + 논리해시)? +
894 "," + JSON.stringify("title")? + ":" + JSON.stringify(요청.제목)? + "\}"
895 let 세트해시바이트 = 해시이백오십육(Bytes.encodeUtf8(투영))?
896 Ok(세트해시바이트.toHex())
899function 이미지계획준비(준비된요청: 준비된이미지요청) -> Result<준비된이미지계획, string> {
900 let 요청 = 준비된요청.요청
901 let 논리 = 준비된요청.논리
902 let 전체행수 = (요청.구조.노드들.length + 11) / 12
903 let 선호 = 선호행경계(요청)
904 let 꼬리 = if 요청.크롬번호 == 1 { 64 } else { 24 }
905 let mut 쪽수 = 1
906 while 쪽수 <= 32 {
907 let mut 논리시작들: Array<int> = []
908 let mut 논리끝들: Array<int> = []
909 let mut 보호길이들: Array<int> = []
910 let mut 용량들: Array<int> = []
911 let mut 유효 = true
912 let mut 쪽번호 = 0
913 while 쪽번호 < 쪽수 {
914 let 시작 = (논리.length() * 쪽번호) / 쪽수
915 let 끝 = (논리.length() * (쪽번호 + 1)) / 쪽수
916 if 끝 <= 시작 { 유효 = false }
917 let 길이 = 보호길이(끝 - 시작)
918 let 띠행수 = 큰값(1, (길이 + 161) / 162)
919 let 용량 = (2800 - (89 + 꼬리) - 띠행수 * 2) / 36
920 if 용량 < 1 { 유효 = false }
921 논리시작들.push(시작); 논리끝들.push(끝); 보호길이들.push(길이); 용량들.push(용량)
922 쪽번호 = 쪽번호 + 1
923 }
924 let mut 용량합 = 0
925 for 용량 in 용량들 { 용량합 = 용량합 + 용량 }
926 if 유효 && 용량합 >= 전체행수 {
927 let mut 행경계 = [0]
928 let mut 행시작 = 0
929 쪽번호 = 0
930 while 쪽번호 < 쪽수 - 1 {
931 let mut 남은용량 = 0
932 let mut 다음 = 쪽번호 + 1
933 while 다음 < 쪽수 { 남은용량 = 남은용량 + 용량들[다음]; 다음 = 다음 + 1 }
934 let 최소끝 = 큰값(행시작 + 1, 전체행수 - 남은용량)
935 let 최대끝 = 작은값(행시작 + 용량들[쪽번호], 전체행수 - (쪽수 - 쪽번호 - 1))
936 if 최소끝 > 최대끝 { 유효 = false }
937 let mut 행끝 = 최대끝
938 let mut 후보 = 최대끝
939 while 후보 >= 최소끝 {
940 if 배열에있음(선호, 후보) { 행끝 = 후보; 후보 = 최소끝 - 1 }
941 else { 후보 = 후보 - 1 }
942 }
943 행경계.push(행끝)
944 행시작 = 행끝
945 쪽번호 = 쪽번호 + 1
946 }
947 if 유효 {
948 행경계.push(전체행수)
949 let mut 쪽들: Array<이미지쪽계획> = []
950 쪽번호 = 0
951 while 쪽번호 < 쪽수 {
952 let 노드시작 = 행경계[쪽번호] * 12
953 let 노드끝 = 작은값(요청.구조.노드들.length, 행경계[쪽번호 + 1] * 12)
954 let 배치 = 이미지배치계산(노드끝 - 노드시작, 보호길이들[쪽번호], 요청.크롬번호)?
955 쪽들.push(이미지쪽계획 {
956 쪽번호: 쪽번호, 노드시작: 노드시작, 노드끝: 노드끝,
957 논리시작: 논리시작들[쪽번호], 논리끝: 논리끝들[쪽번호],
958 보호길이: 보호길이들[쪽번호], 패리티길이: (보호길이들[쪽번호] / 255) * 32,
959 배치: 배치
960 })
961 쪽번호 = 쪽번호 + 1
962 }
963 let 세트해시 = 이미지세트해시계산(요청, 논리, 쪽들)?
964 return Ok(준비된이미지계획 {
965 요청: 준비된요청,
966 계획: 이미지계획 { 쪽들: 쪽들, 세트해시: 세트해시 }
967 })
968 }
969 }
970 쪽수 = 쪽수 + 1
971 }
972 Err("이미지 세트가 32쪽 한계를 넘습니다")
975export function 이미지계획하기(요청글: string, 논리: Bytes) -> Result<string, string> {
976 let 준비된요청 = 이미지요청준비(요청글, 논리)?
977 let 준비된계획 = 이미지계획준비(준비된요청)?
978 let 계획 = 준비된계획.계획
979 let mut 높이들: Array<int> = []
980 let mut 노드시작들: Array<int> = []
981 let mut 노드수들: Array<int> = []
982 let mut 논리길이들: Array<int> = []
983 let mut 보호길이들: Array<int> = []
984 let mut 패리티길이들: Array<int> = []
985 for 쪽 in 계획.쪽들 {
986 높이들.push(쪽.배치.높이); 노드시작들.push(쪽.노드시작); 노드수들.push(쪽.노드끝 - 쪽.노드시작)
987 논리길이들.push(쪽.논리끝 - 쪽.논리시작); 보호길이들.push(쪽.보호길이); 패리티길이들.push(쪽.패리티길이)
988 }
989 JSON.stringify(이미지계획출력 {
990 쪽수: 계획.쪽들.length, 세트해시: 계획.세트해시, 높이들: 높이들,
991 노드시작들: 노드시작들, 노드수들: 노드수들, 논리길이들: 논리길이들,
992 보호길이들: 보호길이들, 패리티길이들: 패리티길이들
993 })
996function 이미지계획복원(
997 준비된요청: 준비된이미지요청,
998 출력: 이미지계획출력
999) -> Result<준비된이미지계획, string> {
1000 let 요청 = 준비된요청.요청

SHA256 5837108d966f4fd9a31fbf7d80441b872e10bd332aef3cc2b1dbaa9329623280