| 101 | } |
| 102 | |
| 103 | func (p *parser) ParseBinaryChunk(frag *BinaryFragment) error { |
| 104 | // Binary fragments are encoded as a series of base85 encoded lines. Each |
| 105 | // line starts with a character in [A-Za-z] giving the number of bytes on |
| 106 | // the line, where A = 1 and z = 52, and ends with a newline character. |
| 107 | // |
| 108 | // The base85 encoding means each line is a multiple of 5 characters + 2 |
| 109 | // additional characters for the length byte and the newline. The fragment |
| 110 | // ends with a blank line. |
| 111 | const ( |
| 112 | shortestValidLine = "A00000\n" |
| 113 | maxBytesPerLine = 52 |
| 114 | ) |
| 115 | |
| 116 | var data bytes.Buffer |
| 117 | buf := make([]byte, maxBytesPerLine) |
| 118 | for { |
| 119 | line := p.Line(0) |
| 120 | if line == "\n" { |
| 121 | break |
| 122 | } |
| 123 | if len(line) < len(shortestValidLine) || (len(line)-2)%5 != 0 { |
| 124 | return p.Errorf(0, "binary patch: corrupt data line") |
| 125 | } |
| 126 | |
| 127 | byteCount, seq := int(line[0]), line[1:len(line)-1] |
| 128 | switch { |
| 129 | case 'A' <= byteCount && byteCount <= 'Z': |
| 130 | byteCount = byteCount - 'A' + 1 |
| 131 | case 'a' <= byteCount && byteCount <= 'z': |
| 132 | byteCount = byteCount - 'a' + 27 |
| 133 | default: |
| 134 | return p.Errorf(0, "binary patch: invalid length byte") |
| 135 | } |
| 136 | |
| 137 | // base85 encodes every 4 bytes into 5 characters, with up to 3 bytes of end padding |
| 138 | maxByteCount := len(seq) / 5 * 4 |
| 139 | if byteCount > maxByteCount || byteCount < maxByteCount-3 { |
| 140 | return p.Errorf(0, "binary patch: incorrect byte count") |
| 141 | } |
| 142 | |
| 143 | if err := base85Decode(buf[:byteCount], []byte(seq)); err != nil { |
| 144 | return p.Errorf(0, "binary patch: %v", err) |
| 145 | } |
| 146 | data.Write(buf[:byteCount]) |
| 147 | |
| 148 | if err := p.Next(); err != nil { |
| 149 | if err == io.EOF { |
| 150 | return p.Errorf(0, "binary patch: unexpected EOF") |
| 151 | } |
| 152 | return err |
| 153 | } |
| 154 | } |
| 155 | |
| 156 | if err := inflateBinaryChunk(frag, &data); err != nil { |
| 157 | return p.Errorf(0, "binary patch: %v", err) |
| 158 | } |
| 159 | |
| 160 | // consume the empty line that ended the fragment |