github.com/letsencrypt/go@v0.0.0-20160714163537-4054769a31f6/src/compress/flate/huffman_bit_writer.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package flate 6 7 import ( 8 "io" 9 ) 10 11 const ( 12 // The largest offset code. 13 offsetCodeCount = 30 14 15 // The special code used to mark the end of a block. 16 endBlockMarker = 256 17 18 // The first length code. 19 lengthCodesStart = 257 20 21 // The number of codegen codes. 22 codegenCodeCount = 19 23 badCode = 255 24 25 // bufferFlushSize indicates the buffer size 26 // after which bytes are flushed to the writer. 27 // Should preferably be a multiple of 6, since 28 // we accumulate 6 bytes between writes to the buffer. 29 bufferFlushSize = 240 30 31 // bufferSize is the actual output byte buffer size. 32 // It must have additional headroom for a flush 33 // which can contain up to 8 bytes. 34 bufferSize = bufferFlushSize + 8 35 ) 36 37 // The number of extra bits needed by length code X - LENGTH_CODES_START. 38 var lengthExtraBits = []int8{ 39 /* 257 */ 0, 0, 0, 40 /* 260 */ 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 41 /* 270 */ 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 42 /* 280 */ 4, 5, 5, 5, 5, 0, 43 } 44 45 // The length indicated by length code X - LENGTH_CODES_START. 46 var lengthBase = []uint32{ 47 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 48 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 49 64, 80, 96, 112, 128, 160, 192, 224, 255, 50 } 51 52 // offset code word extra bits. 53 var offsetExtraBits = []int8{ 54 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 55 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 56 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 57 /* extended window */ 58 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 59 } 60 61 var offsetBase = []uint32{ 62 /* normal deflate */ 63 0x000000, 0x000001, 0x000002, 0x000003, 0x000004, 64 0x000006, 0x000008, 0x00000c, 0x000010, 0x000018, 65 0x000020, 0x000030, 0x000040, 0x000060, 0x000080, 66 0x0000c0, 0x000100, 0x000180, 0x000200, 0x000300, 67 0x000400, 0x000600, 0x000800, 0x000c00, 0x001000, 68 0x001800, 0x002000, 0x003000, 0x004000, 0x006000, 69 70 /* extended window */ 71 0x008000, 0x00c000, 0x010000, 0x018000, 0x020000, 72 0x030000, 0x040000, 0x060000, 0x080000, 0x0c0000, 73 0x100000, 0x180000, 0x200000, 0x300000, 74 } 75 76 // The odd order in which the codegen code sizes are written. 77 var codegenOrder = []uint32{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15} 78 79 type huffmanBitWriter struct { 80 w io.Writer 81 // Data waiting to be written is bytes[0:nbytes] 82 // and then the low nbits of bits. 83 bits uint64 84 nbits uint 85 bytes [bufferSize]byte 86 codegenFreq [codegenCodeCount]int32 87 nbytes int 88 literalFreq []int32 89 offsetFreq []int32 90 codegen []uint8 91 literalEncoding *huffmanEncoder 92 offsetEncoding *huffmanEncoder 93 codegenEncoding *huffmanEncoder 94 err error 95 } 96 97 func newHuffmanBitWriter(w io.Writer) *huffmanBitWriter { 98 return &huffmanBitWriter{ 99 w: w, 100 literalFreq: make([]int32, maxNumLit), 101 offsetFreq: make([]int32, offsetCodeCount), 102 codegen: make([]uint8, maxNumLit+offsetCodeCount+1), 103 literalEncoding: newHuffmanEncoder(maxNumLit), 104 codegenEncoding: newHuffmanEncoder(codegenCodeCount), 105 offsetEncoding: newHuffmanEncoder(offsetCodeCount), 106 } 107 } 108 109 func (w *huffmanBitWriter) reset(writer io.Writer) { 110 w.w = writer 111 w.bits, w.nbits, w.nbytes, w.err = 0, 0, 0, nil 112 w.bytes = [bufferSize]byte{} 113 } 114 115 func (w *huffmanBitWriter) flush() { 116 if w.err != nil { 117 w.nbits = 0 118 return 119 } 120 n := w.nbytes 121 for w.nbits != 0 { 122 w.bytes[n] = byte(w.bits) 123 w.bits >>= 8 124 if w.nbits > 8 { // Avoid underflow 125 w.nbits -= 8 126 } else { 127 w.nbits = 0 128 } 129 n++ 130 } 131 w.bits = 0 132 _, w.err = w.w.Write(w.bytes[:n]) 133 w.nbytes = 0 134 } 135 136 func (w *huffmanBitWriter) writeBits(b int32, nb uint) { 137 w.bits |= uint64(b) << w.nbits 138 w.nbits += nb 139 if w.nbits >= 48 { 140 bits := w.bits 141 w.bits >>= 48 142 w.nbits -= 48 143 n := w.nbytes 144 bytes := w.bytes[n : n+6] 145 bytes[0] = byte(bits) 146 bytes[1] = byte(bits >> 8) 147 bytes[2] = byte(bits >> 16) 148 bytes[3] = byte(bits >> 24) 149 bytes[4] = byte(bits >> 32) 150 bytes[5] = byte(bits >> 40) 151 n += 6 152 if n >= bufferFlushSize { 153 _, w.err = w.w.Write(w.bytes[:n]) 154 n = 0 155 } 156 w.nbytes = n 157 } 158 } 159 160 func (w *huffmanBitWriter) writeBytes(bytes []byte) { 161 if w.err != nil { 162 return 163 } 164 n := w.nbytes 165 if w.nbits&7 != 0 { 166 w.err = InternalError("writeBytes with unfinished bits") 167 return 168 } 169 for w.nbits != 0 { 170 w.bytes[n] = byte(w.bits) 171 w.bits >>= 8 172 w.nbits -= 8 173 n++ 174 } 175 if n != 0 { 176 _, w.err = w.w.Write(w.bytes[:n]) 177 if w.err != nil { 178 return 179 } 180 } 181 w.nbytes = 0 182 _, w.err = w.w.Write(bytes) 183 } 184 185 // RFC 1951 3.2.7 specifies a special run-length encoding for specifying 186 // the literal and offset lengths arrays (which are concatenated into a single 187 // array). This method generates that run-length encoding. 188 // 189 // The result is written into the codegen array, and the frequencies 190 // of each code is written into the codegenFreq array. 191 // Codes 0-15 are single byte codes. Codes 16-18 are followed by additional 192 // information. Code badCode is an end marker 193 // 194 // numLiterals The number of literals in literalEncoding 195 // numOffsets The number of offsets in offsetEncoding 196 // litenc, offenc The literal and offset encoder to use 197 func (w *huffmanBitWriter) generateCodegen(numLiterals int, numOffsets int, litEnc, offEnc *huffmanEncoder) { 198 for i := range w.codegenFreq { 199 w.codegenFreq[i] = 0 200 } 201 // Note that we are using codegen both as a temporary variable for holding 202 // a copy of the frequencies, and as the place where we put the result. 203 // This is fine because the output is always shorter than the input used 204 // so far. 205 codegen := w.codegen // cache 206 // Copy the concatenated code sizes to codegen. Put a marker at the end. 207 cgnl := codegen[:numLiterals] 208 for i := range cgnl { 209 cgnl[i] = uint8(litEnc.codes[i].len) 210 } 211 212 cgnl = codegen[numLiterals : numLiterals+numOffsets] 213 for i := range cgnl { 214 cgnl[i] = uint8(offEnc.codes[i].len) 215 } 216 codegen[numLiterals+numOffsets] = badCode 217 218 size := codegen[0] 219 count := 1 220 outIndex := 0 221 for inIndex := 1; size != badCode; inIndex++ { 222 // INVARIANT: We have seen "count" copies of size that have not yet 223 // had output generated for them. 224 nextSize := codegen[inIndex] 225 if nextSize == size { 226 count++ 227 continue 228 } 229 // We need to generate codegen indicating "count" of size. 230 if size != 0 { 231 codegen[outIndex] = size 232 outIndex++ 233 w.codegenFreq[size]++ 234 count-- 235 for count >= 3 { 236 n := 6 237 if n > count { 238 n = count 239 } 240 codegen[outIndex] = 16 241 outIndex++ 242 codegen[outIndex] = uint8(n - 3) 243 outIndex++ 244 w.codegenFreq[16]++ 245 count -= n 246 } 247 } else { 248 for count >= 11 { 249 n := 138 250 if n > count { 251 n = count 252 } 253 codegen[outIndex] = 18 254 outIndex++ 255 codegen[outIndex] = uint8(n - 11) 256 outIndex++ 257 w.codegenFreq[18]++ 258 count -= n 259 } 260 if count >= 3 { 261 // count >= 3 && count <= 10 262 codegen[outIndex] = 17 263 outIndex++ 264 codegen[outIndex] = uint8(count - 3) 265 outIndex++ 266 w.codegenFreq[17]++ 267 count = 0 268 } 269 } 270 count-- 271 for ; count >= 0; count-- { 272 codegen[outIndex] = size 273 outIndex++ 274 w.codegenFreq[size]++ 275 } 276 // Set up invariant for next time through the loop. 277 size = nextSize 278 count = 1 279 } 280 // Marker indicating the end of the codegen. 281 codegen[outIndex] = badCode 282 } 283 284 // dynamicSize returns the size of dynamically encoded data in bits. 285 func (w *huffmanBitWriter) dynamicSize(litEnc, offEnc *huffmanEncoder, extraBits int) (size, numCodegens int) { 286 numCodegens = len(w.codegenFreq) 287 for numCodegens > 4 && w.codegenFreq[codegenOrder[numCodegens-1]] == 0 { 288 numCodegens-- 289 } 290 header := 3 + 5 + 5 + 4 + (3 * numCodegens) + 291 w.codegenEncoding.bitLength(w.codegenFreq[:]) + 292 int(w.codegenFreq[16])*2 + 293 int(w.codegenFreq[17])*3 + 294 int(w.codegenFreq[18])*7 295 size = header + 296 litEnc.bitLength(w.literalFreq) + 297 offEnc.bitLength(w.offsetFreq) + 298 extraBits 299 300 return size, numCodegens 301 } 302 303 // fixedSize returns the size of dynamically encoded data in bits. 304 func (w *huffmanBitWriter) fixedSize(extraBits int) int { 305 return 3 + 306 fixedLiteralEncoding.bitLength(w.literalFreq) + 307 fixedOffsetEncoding.bitLength(w.offsetFreq) + 308 extraBits 309 } 310 311 // storedSize calculates the stored size, including header. 312 // The function returns the size in bits and whether the block 313 // fits inside a single block. 314 func (w *huffmanBitWriter) storedSize(in []byte) (int, bool) { 315 if in == nil { 316 return 0, false 317 } 318 if len(in) <= maxStoreBlockSize { 319 return (len(in) + 5) * 8, true 320 } 321 return 0, false 322 } 323 324 func (w *huffmanBitWriter) writeCode(c hcode) { 325 if w.err != nil { 326 return 327 } 328 w.bits |= uint64(c.code) << w.nbits 329 w.nbits += uint(c.len) 330 if w.nbits >= 48 { 331 bits := w.bits 332 w.bits >>= 48 333 w.nbits -= 48 334 n := w.nbytes 335 bytes := w.bytes[n : n+6] 336 bytes[0] = byte(bits) 337 bytes[1] = byte(bits >> 8) 338 bytes[2] = byte(bits >> 16) 339 bytes[3] = byte(bits >> 24) 340 bytes[4] = byte(bits >> 32) 341 bytes[5] = byte(bits >> 40) 342 n += 6 343 if n >= bufferFlushSize { 344 _, w.err = w.w.Write(w.bytes[:n]) 345 n = 0 346 } 347 w.nbytes = n 348 } 349 } 350 351 // Write the header of a dynamic Huffman block to the output stream. 352 // 353 // numLiterals The number of literals specified in codegen 354 // numOffsets The number of offsets specified in codegen 355 // numCodegens The number of codegens used in codegen 356 func (w *huffmanBitWriter) writeDynamicHeader(numLiterals int, numOffsets int, numCodegens int, isEof bool) { 357 if w.err != nil { 358 return 359 } 360 var firstBits int32 = 4 361 if isEof { 362 firstBits = 5 363 } 364 w.writeBits(firstBits, 3) 365 w.writeBits(int32(numLiterals-257), 5) 366 w.writeBits(int32(numOffsets-1), 5) 367 w.writeBits(int32(numCodegens-4), 4) 368 369 for i := 0; i < numCodegens; i++ { 370 value := uint(w.codegenEncoding.codes[codegenOrder[i]].len) 371 w.writeBits(int32(value), 3) 372 } 373 374 i := 0 375 for { 376 var codeWord int = int(w.codegen[i]) 377 i++ 378 if codeWord == badCode { 379 break 380 } 381 w.writeCode(w.codegenEncoding.codes[uint32(codeWord)]) 382 383 switch codeWord { 384 case 16: 385 w.writeBits(int32(w.codegen[i]), 2) 386 i++ 387 break 388 case 17: 389 w.writeBits(int32(w.codegen[i]), 3) 390 i++ 391 break 392 case 18: 393 w.writeBits(int32(w.codegen[i]), 7) 394 i++ 395 break 396 } 397 } 398 } 399 400 func (w *huffmanBitWriter) writeStoredHeader(length int, isEof bool) { 401 if w.err != nil { 402 return 403 } 404 var flag int32 405 if isEof { 406 flag = 1 407 } 408 w.writeBits(flag, 3) 409 w.flush() 410 w.writeBits(int32(length), 16) 411 w.writeBits(int32(^uint16(length)), 16) 412 } 413 414 func (w *huffmanBitWriter) writeFixedHeader(isEof bool) { 415 if w.err != nil { 416 return 417 } 418 // Indicate that we are a fixed Huffman block 419 var value int32 = 2 420 if isEof { 421 value = 3 422 } 423 w.writeBits(value, 3) 424 } 425 426 // writeBlock will write a block of tokens with the smallest encoding. 427 // The original input can be supplied, and if the huffman encoded data 428 // is larger than the original bytes, the data will be written as a 429 // stored block. 430 // If the input is nil, the tokens will always be Huffman encoded. 431 func (w *huffmanBitWriter) writeBlock(tokens []token, eof bool, input []byte) { 432 if w.err != nil { 433 return 434 } 435 436 tokens = append(tokens, endBlockMarker) 437 numLiterals, numOffsets := w.indexTokens(tokens) 438 439 var extraBits int 440 storedSize, storable := w.storedSize(input) 441 if storable { 442 // We only bother calculating the costs of the extra bits required by 443 // the length of offset fields (which will be the same for both fixed 444 // and dynamic encoding), if we need to compare those two encodings 445 // against stored encoding. 446 for lengthCode := lengthCodesStart + 8; lengthCode < numLiterals; lengthCode++ { 447 // First eight length codes have extra size = 0. 448 extraBits += int(w.literalFreq[lengthCode]) * int(lengthExtraBits[lengthCode-lengthCodesStart]) 449 } 450 for offsetCode := 4; offsetCode < numOffsets; offsetCode++ { 451 // First four offset codes have extra size = 0. 452 extraBits += int(w.offsetFreq[offsetCode]) * int(offsetExtraBits[offsetCode]) 453 } 454 } 455 456 // Figure out smallest code. 457 // Fixed Huffman baseline. 458 var literalEncoding = fixedLiteralEncoding 459 var offsetEncoding = fixedOffsetEncoding 460 var size = w.fixedSize(extraBits) 461 462 // Dynamic Huffman? 463 var numCodegens int 464 465 // Generate codegen and codegenFrequencies, which indicates how to encode 466 // the literalEncoding and the offsetEncoding. 467 w.generateCodegen(numLiterals, numOffsets, w.literalEncoding, w.offsetEncoding) 468 w.codegenEncoding.generate(w.codegenFreq[:], 7) 469 dynamicSize, numCodegens := w.dynamicSize(w.literalEncoding, w.offsetEncoding, extraBits) 470 471 if dynamicSize < size { 472 size = dynamicSize 473 literalEncoding = w.literalEncoding 474 offsetEncoding = w.offsetEncoding 475 } 476 477 // Stored bytes? 478 if storable && storedSize < size { 479 w.writeStoredHeader(len(input), eof) 480 w.writeBytes(input) 481 return 482 } 483 484 // Huffman. 485 if literalEncoding == fixedLiteralEncoding { 486 w.writeFixedHeader(eof) 487 } else { 488 w.writeDynamicHeader(numLiterals, numOffsets, numCodegens, eof) 489 } 490 491 // Write the tokens. 492 w.writeTokens(tokens, literalEncoding.codes, offsetEncoding.codes) 493 } 494 495 // writeBlockDynamic encodes a block using a dynamic Huffman table. 496 // This should be used if the symbols used have a disproportionate 497 // histogram distribution. 498 // If input is supplied and the compression savings are below 1/16th of the 499 // input size the block is stored. 500 func (w *huffmanBitWriter) writeBlockDynamic(tokens []token, eof bool, input []byte) { 501 if w.err != nil { 502 return 503 } 504 505 tokens = append(tokens, endBlockMarker) 506 numLiterals, numOffsets := w.indexTokens(tokens) 507 508 // Generate codegen and codegenFrequencies, which indicates how to encode 509 // the literalEncoding and the offsetEncoding. 510 w.generateCodegen(numLiterals, numOffsets, w.literalEncoding, w.offsetEncoding) 511 w.codegenEncoding.generate(w.codegenFreq[:], 7) 512 size, numCodegens := w.dynamicSize(w.literalEncoding, huffOffset, 0) 513 514 // Store bytes, if we don't get a reasonable improvement. 515 if ssize, storable := w.storedSize(input); storable && ssize < (size+size>>4) { 516 w.writeStoredHeader(len(input), eof) 517 w.writeBytes(input) 518 return 519 } 520 521 // Write Huffman table. 522 w.writeDynamicHeader(numLiterals, numOffsets, numCodegens, eof) 523 524 // Write the tokens. 525 w.writeTokens(tokens, w.literalEncoding.codes, w.offsetEncoding.codes) 526 } 527 528 // indexTokens indexes a slice of tokens, and updates 529 // literalFreq and offsetFreq, and generates literalEncoding 530 // and offsetEncoding. 531 // The number of literal and offset tokens is returned. 532 func (w *huffmanBitWriter) indexTokens(tokens []token) (numLiterals, numOffsets int) { 533 for i := range w.literalFreq { 534 w.literalFreq[i] = 0 535 } 536 for i := range w.offsetFreq { 537 w.offsetFreq[i] = 0 538 } 539 540 for _, t := range tokens { 541 if t < matchType { 542 w.literalFreq[t.literal()]++ 543 continue 544 } 545 length := t.length() 546 offset := t.offset() 547 w.literalFreq[lengthCodesStart+lengthCode(length)]++ 548 w.offsetFreq[offsetCode(offset)]++ 549 } 550 551 // get the number of literals 552 numLiterals = len(w.literalFreq) 553 for w.literalFreq[numLiterals-1] == 0 { 554 numLiterals-- 555 } 556 // get the number of offsets 557 numOffsets = len(w.offsetFreq) 558 for numOffsets > 0 && w.offsetFreq[numOffsets-1] == 0 { 559 numOffsets-- 560 } 561 if numOffsets == 0 { 562 // We haven't found a single match. If we want to go with the dynamic encoding, 563 // we should count at least one offset to be sure that the offset huffman tree could be encoded. 564 w.offsetFreq[0] = 1 565 numOffsets = 1 566 } 567 w.literalEncoding.generate(w.literalFreq, 15) 568 w.offsetEncoding.generate(w.offsetFreq, 15) 569 return 570 } 571 572 // writeTokens writes a slice of tokens to the output. 573 // codes for literal and offset encoding must be supplied. 574 func (w *huffmanBitWriter) writeTokens(tokens []token, leCodes, oeCodes []hcode) { 575 for _, t := range tokens { 576 if t < matchType { 577 w.writeCode(leCodes[t.literal()]) 578 continue 579 } 580 // Write the length 581 length := t.length() 582 lengthCode := lengthCode(length) 583 w.writeCode(leCodes[lengthCode+lengthCodesStart]) 584 extraLengthBits := uint(lengthExtraBits[lengthCode]) 585 if extraLengthBits > 0 { 586 extraLength := int32(length - lengthBase[lengthCode]) 587 w.writeBits(extraLength, extraLengthBits) 588 } 589 // Write the offset 590 offset := t.offset() 591 offsetCode := offsetCode(offset) 592 w.writeCode(oeCodes[offsetCode]) 593 extraOffsetBits := uint(offsetExtraBits[offsetCode]) 594 if extraOffsetBits > 0 { 595 extraOffset := int32(offset - offsetBase[offsetCode]) 596 w.writeBits(extraOffset, extraOffsetBits) 597 } 598 } 599 } 600 601 // huffOffset is a static offset encoder used for huffman only encoding. 602 // It can be reused since we will not be encoding offset values. 603 var huffOffset *huffmanEncoder 604 605 func init() { 606 w := newHuffmanBitWriter(nil) 607 w.offsetFreq[0] = 1 608 huffOffset = newHuffmanEncoder(offsetCodeCount) 609 huffOffset.generate(w.offsetFreq, 15) 610 } 611 612 // writeBlockHuff encodes a block of bytes as either 613 // Huffman encoded literals or uncompressed bytes if the 614 // results only gains very little from compression. 615 func (w *huffmanBitWriter) writeBlockHuff(eof bool, input []byte) { 616 if w.err != nil { 617 return 618 } 619 620 // Clear histogram 621 for i := range w.literalFreq { 622 w.literalFreq[i] = 0 623 } 624 625 // Add everything as literals 626 histogram(input, w.literalFreq) 627 628 w.literalFreq[endBlockMarker] = 1 629 630 const numLiterals = endBlockMarker + 1 631 const numOffsets = 1 632 633 w.literalEncoding.generate(w.literalFreq, 15) 634 635 // Figure out smallest code. 636 // Always use dynamic Huffman or Store 637 var numCodegens int 638 639 // Generate codegen and codegenFrequencies, which indicates how to encode 640 // the literalEncoding and the offsetEncoding. 641 w.generateCodegen(numLiterals, numOffsets, w.literalEncoding, huffOffset) 642 w.codegenEncoding.generate(w.codegenFreq[:], 7) 643 size, numCodegens := w.dynamicSize(w.literalEncoding, huffOffset, 0) 644 645 // Store bytes, if we don't get a reasonable improvement. 646 if ssize, storable := w.storedSize(input); storable && ssize < (size+size>>4) { 647 w.writeStoredHeader(len(input), eof) 648 w.writeBytes(input) 649 return 650 } 651 652 // Huffman. 653 w.writeDynamicHeader(numLiterals, numOffsets, numCodegens, eof) 654 encoding := w.literalEncoding.codes[:257] 655 n := w.nbytes 656 for _, t := range input { 657 // Bitwriting inlined, ~30% speedup 658 c := encoding[t] 659 w.bits |= uint64(c.code) << w.nbits 660 w.nbits += uint(c.len) 661 if w.nbits < 48 { 662 continue 663 } 664 // Store 6 bytes 665 bits := w.bits 666 w.bits >>= 48 667 w.nbits -= 48 668 bytes := w.bytes[n : n+6] 669 bytes[0] = byte(bits) 670 bytes[1] = byte(bits >> 8) 671 bytes[2] = byte(bits >> 16) 672 bytes[3] = byte(bits >> 24) 673 bytes[4] = byte(bits >> 32) 674 bytes[5] = byte(bits >> 40) 675 n += 6 676 if n < bufferFlushSize { 677 continue 678 } 679 _, w.err = w.w.Write(w.bytes[:n]) 680 if w.err != nil { 681 return 682 } 683 n = 0 684 } 685 w.nbytes = n 686 w.writeCode(encoding[endBlockMarker]) 687 } 688 689 // histogram accumulates a histogram of b in h. 690 // 691 // len(h) must be >= 256, and h's elements must be all zeroes. 692 func histogram(b []byte, h []int32) { 693 h = h[:256] 694 for _, t := range b { 695 h[t]++ 696 } 697 }