File: datauri.go 1 /* 2 The MIT License (MIT) 3 4 Copyright (c) 2026 pacman64 5 6 Permission is hereby granted, free of charge, to any person obtaining a copy of 7 this software and associated documentation files (the "Software"), to deal 8 in the Software without restriction, including without limitation the rights to 9 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 10 of the Software, and to permit persons to whom the Software is furnished to do 11 so, subject to the following conditions: 12 13 The above copyright notice and this permission notice shall be included in all 14 copies or substantial portions of the Software. 15 16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 SOFTWARE. 23 */ 24 25 /* 26 To compile a smaller-sized command-line app, you can use the `go` command as 27 follows: 28 29 go build -ldflags "-s -w" -trimpath datauri.go 30 */ 31 32 package main 33 34 import ( 35 "bufio" 36 "bytes" 37 "encoding/base64" 38 "errors" 39 "io" 40 "os" 41 "strings" 42 ) 43 44 const info = ` 45 datauri [options...] [filenames...] 46 47 48 Encode bytes as data-URIs, auto-detecting the file/data type using the first 49 few bytes from each data/file stream. When given multiple inputs, the output 50 will be multiple lines, one for each file given. 51 52 Empty files/inputs result in empty lines. A simple dash (-) stands for the 53 standard-input, which is also used automatically when not given any files. 54 55 Data-URIs are base64-encoded text representations of arbitrary data, which 56 include their payload's MIME-type, and which are directly useable/shareable 57 in web-browsers as links, despite not looking like normal links/URIs. 58 59 Some web-browsers limit the size of handled data-URIs to tens of kilobytes. 60 61 62 Options 63 64 -h, -help, --h, --help show this help message 65 ` 66 67 func main() { 68 if len(os.Args) > 1 { 69 switch os.Args[1] { 70 case `-h`, `--h`, `-help`, `--help`: 71 os.Stdout.WriteString(info[1:]) 72 return 73 } 74 } 75 76 if err := run(os.Stdout, os.Args[1:]); err != nil && err != io.EOF { 77 os.Stderr.WriteString(err.Error()) 78 os.Stderr.WriteString("\n") 79 os.Exit(1) 80 } 81 } 82 83 func run(w io.Writer, args []string) error { 84 bw := bufio.NewWriter(w) 85 defer bw.Flush() 86 87 if len(args) == 0 { 88 return dataURI(bw, os.Stdin, `<stdin>`) 89 } 90 91 for _, name := range args { 92 if err := handleFile(bw, name); err != nil { 93 return err 94 } 95 } 96 return nil 97 } 98 99 func handleFile(w *bufio.Writer, name string) error { 100 if name == `` || name == `-` { 101 return dataURI(w, os.Stdin, `<stdin>`) 102 } 103 104 f, err := os.Open(name) 105 if err != nil { 106 return errors.New(`can't read from file named "` + name + `"`) 107 } 108 defer f.Close() 109 110 return dataURI(w, f, name) 111 } 112 113 func dataURI(w *bufio.Writer, r io.Reader, name string) error { 114 var buf [64]byte 115 n, err := r.Read(buf[:]) 116 if err != nil && err != io.EOF { 117 return err 118 } 119 start := buf[:n] 120 121 // handle regular data, trying to auto-detect its MIME type using 122 // its first few bytes 123 mime, ok := detectMIME(start) 124 if !ok { 125 return errors.New(name + `: unknown file type`) 126 } 127 128 w.WriteString(`data:`) 129 w.WriteString(mime) 130 w.WriteString(`;base64,`) 131 r = io.MultiReader(bytes.NewReader(start), r) 132 enc := base64.NewEncoder(base64.StdEncoding, w) 133 if _, err := io.Copy(enc, r); err != nil { 134 return err 135 } 136 enc.Close() 137 138 w.WriteByte('\n') 139 if err := w.Flush(); err != nil { 140 return io.EOF 141 } 142 return nil 143 } 144 145 // makeDotless is similar to filepath.Ext, except its results never start 146 // with a dot 147 func makeDotless(s string) string { 148 i := strings.LastIndexByte(s, '.') 149 if i >= 0 { 150 return s[(i + 1):] 151 } 152 return s 153 } 154 155 // hasPrefixByte is a simpler, single-byte version of bytes.HasPrefix 156 func hasPrefixByte(b []byte, prefix byte) bool { 157 return len(b) > 0 && b[0] == prefix 158 } 159 160 // hasPrefixFold is a case-insensitive bytes.HasPrefix 161 func hasPrefixFold(s []byte, prefix []byte) bool { 162 n := len(prefix) 163 return len(s) >= n && bytes.EqualFold(s[:n], prefix) 164 } 165 166 // trimLeadingWhitespace ignores leading space-like symbols: this is useful 167 // to handle text-based data formats more flexibly 168 func trimLeadingWhitespace(b []byte) []byte { 169 for len(b) > 0 { 170 switch b[0] { 171 case ' ', '\t', '\n', '\r': 172 b = b[1:] 173 default: 174 return b 175 } 176 } 177 178 // an empty slice is all that's left, at this point 179 return nil 180 } 181 182 // nameToMIME tries to match a MIME type to a filename, dotted file extension, 183 // or a dot-less filetype/extension given 184 func nameToMIME(fname string) (mimeType string, ok bool) { 185 // handle dotless file types and filenames alike 186 kind, ok := type2mime[makeDotless(fname)] 187 return kind, ok 188 } 189 190 // detectMIME guesses the first appropriate MIME type from the first few 191 // data bytes given: 24 bytes are enough to detect all supported types 192 func detectMIME(b []byte) (mimeType string, ok bool) { 193 t, ok := detectType(b) 194 if ok { 195 return t, true 196 } 197 return ``, false 198 } 199 200 // detectType guesses the first appropriate file type for the data given: 201 // here the type is a a filename extension without the leading dot 202 func detectType(b []byte) (dotlessExt string, ok bool) { 203 // empty data, so there's no way to detect anything 204 if len(b) == 0 { 205 return ``, false 206 } 207 208 // check for plain-text web-document formats case-insensitively 209 kind, ok := checkDoc(b) 210 if ok { 211 return kind, true 212 } 213 214 // check data formats which allow any byte at the start 215 kind, ok = checkSpecial(b) 216 if ok { 217 return kind, true 218 } 219 220 // check all other supported data formats 221 headers := hdrDispatch[b[0]] 222 for _, t := range headers { 223 if hasPrefixPattern(b[1:], t.Header[1:], cba) { 224 return t.Type, true 225 } 226 } 227 228 // unrecognized data format 229 return ``, false 230 } 231 232 // checkDoc tries to guess if the bytes given are the start of HTML, SVG, 233 // XML, or JSON data 234 func checkDoc(b []byte) (kind string, ok bool) { 235 // ignore leading whitespaces 236 b = trimLeadingWhitespace(b) 237 238 // can't detect anything with empty data 239 if len(b) == 0 { 240 return ``, false 241 } 242 243 // handle XHTML documents which don't start with a doctype declaration 244 if bytes.Contains(b, doctypeHTML) { 245 return html, true 246 } 247 248 // handle HTML/SVG/XML documents 249 if hasPrefixByte(b, '<') { 250 if hasPrefixFold(b, []byte{'<', '?', 'x', 'm', 'l'}) { 251 if bytes.Contains(b, []byte{'<', 's', 'v', 'g'}) { 252 return svg, true 253 } 254 return xml, true 255 } 256 257 headers := hdrDispatch['<'] 258 for _, v := range headers { 259 if hasPrefixFold(b, v.Header) { 260 return v.Type, true 261 } 262 } 263 return ``, false 264 } 265 266 // handle JSON with top-level arrays 267 if hasPrefixByte(b, '[') { 268 // match [", or [[, or [{, ignoring spaces between 269 b = trimLeadingWhitespace(b[1:]) 270 if len(b) > 0 { 271 switch b[0] { 272 case '"', '[', '{': 273 return json, true 274 } 275 } 276 return ``, false 277 } 278 279 // handle JSON with top-level objects 280 if hasPrefixByte(b, '{') { 281 // match {", ignoring spaces between: after {, the only valid syntax 282 // which can follow is the opening quote for the expected object-key 283 b = trimLeadingWhitespace(b[1:]) 284 if hasPrefixByte(b, '"') { 285 return json, true 286 } 287 return ``, false 288 } 289 290 // checking for a quoted string, any of the JSON keywords, or even a 291 // number seems too ambiguous to declare the data valid JSON 292 293 // no web-document format detected 294 return ``, false 295 } 296 297 // checkSpecial handles special file-format headers, which should be checked 298 // before the normal file-type headers, since the first-byte dispatch algo 299 // doesn't work for these 300 func checkSpecial(b []byte) (kind string, ok bool) { 301 if len(b) >= 8 && bytes.Index(b, []byte{'f', 't', 'y', 'p'}) == 4 { 302 for _, t := range specialHeaders { 303 if hasPrefixPattern(b[4:], t.Header[4:], cba) { 304 return t.Type, true 305 } 306 } 307 } 308 return ``, false 309 } 310 311 // hasPrefixPattern works like bytes.HasPrefix, except it allows for a special 312 // value to signal any byte is allowed on specific spots 313 func hasPrefixPattern(what []byte, pat []byte, wildcard byte) bool { 314 // if the data are shorter than the pattern to match, there's no match 315 if len(what) < len(pat) { 316 return false 317 } 318 319 // use a slice which ensures the pattern length is never exceeded 320 what = what[:len(pat)] 321 322 for i, x := range what { 323 y := pat[i] 324 if x != y && y != wildcard { 325 return false 326 } 327 } 328 return true 329 } 330 331 // all the MIME types used/recognized in this package 332 const ( 333 aiff = `audio/aiff` 334 au = `audio/basic` 335 avi = `video/avi` 336 avif = `image/avif` 337 bmp = `image/x-bmp` 338 caf = `audio/x-caf` 339 cur = `image/vnd.microsoft.icon` 340 css = `text/css` 341 csv = `text/csv` 342 djvu = `image/x-djvu` 343 elf = `application/x-elf` 344 exe = `application/vnd.microsoft.portable-executable` 345 flac = `audio/x-flac` 346 gif = `image/gif` 347 gz = `application/gzip` 348 heic = `image/heic` 349 htm = `text/html` 350 html = `text/html` 351 ico = `image/x-icon` 352 iso = `application/octet-stream` 353 jpg = `image/jpeg` 354 jpeg = `image/jpeg` 355 js = `application/javascript` 356 json = `application/json` 357 m4a = `audio/aac` 358 m4v = `video/x-m4v` 359 mid = `audio/midi` 360 mov = `video/quicktime` 361 mp4 = `video/mp4` 362 mp3 = `audio/mpeg` 363 mpg = `video/mpeg` 364 ogg = `audio/ogg` 365 opus = `audio/opus` 366 pdf = `application/pdf` 367 png = `image/png` 368 ps = `application/postscript` 369 psd = `image/vnd.adobe.photoshop` 370 rtf = `application/rtf` 371 sqlite3 = `application/x-sqlite3` 372 svg = `image/svg+xml` 373 text = `text/plain` 374 tiff = `image/tiff` 375 tsv = `text/tsv` 376 wasm = `application/wasm` 377 wav = `audio/x-wav` 378 webp = `image/webp` 379 webm = `video/webm` 380 xml = `application/xml` 381 zip = `application/zip` 382 zst = `application/zstd` 383 ) 384 385 // type2mime turns dotless format-names into MIME types 386 var type2mime = map[string]string{ 387 `aiff`: aiff, 388 `wav`: wav, 389 `avi`: avi, 390 `jpg`: jpg, 391 `jpeg`: jpeg, 392 `m4a`: m4a, 393 `mp4`: mp4, 394 `m4v`: m4v, 395 `mov`: mov, 396 `png`: png, 397 `avif`: avif, 398 `webp`: webp, 399 `gif`: gif, 400 `tiff`: tiff, 401 `psd`: psd, 402 `flac`: flac, 403 `webm`: webm, 404 `mpg`: mpg, 405 `zip`: zip, 406 `gz`: gz, 407 `zst`: zst, 408 `mp3`: mp3, 409 `opus`: opus, 410 `bmp`: bmp, 411 `mid`: mid, 412 `ogg`: ogg, 413 `html`: html, 414 `htm`: htm, 415 `svg`: svg, 416 `xml`: xml, 417 `rtf`: rtf, 418 `pdf`: pdf, 419 `ps`: ps, 420 `au`: au, 421 `ico`: ico, 422 `cur`: cur, 423 `caf`: caf, 424 `heic`: heic, 425 `sqlite3`: sqlite3, 426 `elf`: elf, 427 `exe`: exe, 428 `wasm`: wasm, 429 `iso`: iso, 430 `txt`: text, 431 `css`: css, 432 `csv`: csv, 433 `tsv`: tsv, 434 `js`: js, 435 `json`: json, 436 `geojson`: json, 437 } 438 439 // formatDescriptor ties a file-header pattern to its data-format type 440 type formatDescriptor struct { 441 Header []byte 442 Type string 443 } 444 445 // can be anything: ensure this value differs from all other literal bytes 446 // in the generic-headers table: failing that, its value could cause subtle 447 // type-misdetection bugs 448 const cba = 0xFD // 253, which is > 127, the highest-valued ascii symbol 449 450 // dash-streamed m4a format 451 var m4aDash = []byte{ 452 cba, cba, cba, cba, 'f', 't', 'y', 'p', 'd', 'a', 's', 'h', 453 000, 000, 000, 000, 'i', 's', 'o', '6', 'm', 'p', '4', '1', 454 } 455 456 // format markers with leading wildcards, which should be checked before the 457 // normal ones: this is to prevent mismatches with the latter types, even 458 // though you can make probabilistic arguments which suggest these mismatches 459 // should be very unlikely in practice 460 var specialHeaders = []formatDescriptor{ 461 {[]byte{cba, cba, cba, cba, 'f', 't', 'y', 'p', 'M', '4', 'A', ' '}, m4a}, 462 {[]byte{cba, cba, cba, cba, 'f', 't', 'y', 'p', 'M', '4', 'A', 000}, m4a}, 463 {[]byte{cba, cba, cba, cba, 'f', 't', 'y', 'p', 'M', 'S', 'N', 'V'}, mp4}, 464 {[]byte{cba, cba, cba, cba, 'f', 't', 'y', 'p', 'i', 's', 'o', 'm'}, mp4}, 465 {[]byte{cba, cba, cba, cba, 'f', 't', 'y', 'p', 'm', 'p', '4', '2'}, m4v}, 466 {[]byte{cba, cba, cba, cba, 'f', 't', 'y', 'p', 'q', 't', ' ', ' '}, mov}, 467 {[]byte{cba, cba, cba, cba, 'f', 't', 'y', 'p', 'h', 'e', 'i', 'c'}, heic}, 468 {[]byte{cba, cba, cba, cba, 'f', 't', 'y', 'p', 'a', 'v', 'i', 'f'}, avif}, 469 {m4aDash, m4a}, 470 } 471 472 // sqlite3 database format 473 var sqlite3db = []byte{ 474 'S', 'Q', 'L', 'i', 't', 'e', ' ', 475 'f', 'o', 'r', 'm', 'a', 't', ' ', '3', 476 000, 477 } 478 479 // windows-variant bitmap file-header, which is followed by a byte-counter for 480 // the 40-byte infoheader which follows that 481 var winbmp = []byte{ 482 'B', 'M', cba, cba, cba, cba, cba, cba, cba, cba, cba, cba, cba, cba, 40, 483 } 484 485 // deja-vu document format 486 var djv = []byte{ 487 'A', 'T', '&', 'T', 'F', 'O', 'R', 'M', cba, cba, cba, cba, 'D', 'J', 'V', 488 } 489 490 var doctypeHTML = []byte{ 491 '<', '!', 'D', 'O', 'C', 'T', 'Y', 'P', 'E', ' ', 'h', 't', 'm', 'l', 492 } 493 494 // hdrDispatch groups format-description-groups by their first byte, thus 495 // shortening total lookups for some data header: notice how the `ftyp` data 496 // formats aren't handled here, since these can start with any byte, instead 497 // of the literal value of the any-byte markers they use 498 var hdrDispatch = [256][]formatDescriptor{ 499 { 500 {[]byte{000, 000, 001, 0xBA}, mpg}, 501 {[]byte{000, 000, 001, 0xB3}, mpg}, 502 {[]byte{000, 000, 001, 000}, ico}, 503 {[]byte{000, 000, 002, 000}, cur}, 504 {[]byte{000, 'a', 's', 'm'}, wasm}, 505 }, // 0 506 nil, // 1 507 nil, // 2 508 nil, // 3 509 nil, // 4 510 nil, // 5 511 nil, // 6 512 nil, // 7 513 nil, // 8 514 nil, // 9 515 nil, // 10 516 nil, // 11 517 nil, // 12 518 nil, // 13 519 nil, // 14 520 nil, // 15 521 nil, // 16 522 nil, // 17 523 nil, // 18 524 nil, // 19 525 nil, // 20 526 nil, // 21 527 nil, // 22 528 nil, // 23 529 nil, // 24 530 nil, // 25 531 { 532 {[]byte{0x1A, 0x45, 0xDF, 0xA3}, webm}, 533 }, // 26 534 nil, // 27 535 nil, // 28 536 nil, // 29 537 nil, // 30 538 { 539 // {[]byte{0x1F, 0x8B, 0x08, 0x08}, gz}, 540 {[]byte{0x1F, 0x8B, 0x08}, gz}, 541 }, // 31 542 nil, // 32 543 nil, // 33 ! 544 nil, // 34 " 545 { 546 {[]byte{'#', '!', ' '}, text}, 547 {[]byte{'#', '!', '/'}, text}, 548 }, // 35 # 549 nil, // 36 $ 550 { 551 {[]byte{'%', 'P', 'D', 'F'}, pdf}, 552 {[]byte{'%', '!', 'P', 'S'}, ps}, 553 }, // 37 % 554 nil, // 38 & 555 nil, // 39 ' 556 { 557 {[]byte{0x28, 0xB5, 0x2F, 0xFD}, zst}, 558 }, // 40 ( 559 nil, // 41 ) 560 nil, // 42 * 561 nil, // 43 + 562 nil, // 44 , 563 nil, // 45 - 564 { 565 {[]byte{'.', 's', 'n', 'd'}, au}, 566 }, // 46 . 567 nil, // 47 / 568 nil, // 48 0 569 nil, // 49 1 570 nil, // 50 2 571 nil, // 51 3 572 nil, // 52 4 573 nil, // 53 5 574 nil, // 54 6 575 nil, // 55 7 576 { 577 {[]byte{'8', 'B', 'P', 'S'}, psd}, 578 }, // 56 8 579 nil, // 57 9 580 nil, // 58 : 581 nil, // 59 ; 582 { 583 // func checkDoc is better for these, since it's case-insensitive 584 {doctypeHTML, html}, 585 {[]byte{'<', 's', 'v', 'g'}, svg}, 586 {[]byte{'<', 'h', 't', 'm', 'l', '>'}, html}, 587 {[]byte{'<', 'h', 'e', 'a', 'd', '>'}, html}, 588 {[]byte{'<', 'b', 'o', 'd', 'y', '>'}, html}, 589 {[]byte{'<', '?', 'x', 'm', 'l'}, xml}, 590 }, // 60 < 591 nil, // 61 = 592 nil, // 62 > 593 nil, // 63 ? 594 nil, // 64 @ 595 { 596 {djv, djvu}, 597 }, // 65 A 598 { 599 {winbmp, bmp}, 600 }, // 66 B 601 nil, // 67 C 602 nil, // 68 D 603 nil, // 69 E 604 { 605 {[]byte{'F', 'O', 'R', 'M', cba, cba, cba, cba, 'A', 'I', 'F', 'F'}, aiff}, 606 {[]byte{'F', 'O', 'R', 'M', cba, cba, cba, cba, 'A', 'I', 'F', 'C'}, aiff}, 607 }, // 70 F 608 { 609 {[]byte{'G', 'I', 'F', '8', '7', 'a'}, gif}, 610 {[]byte{'G', 'I', 'F', '8', '9', 'a'}, gif}, 611 }, // 71 G 612 nil, // 72 H 613 { 614 {[]byte{'I', 'D', '3', 2}, mp3}, // ID3-format metadata 615 {[]byte{'I', 'D', '3', 3}, mp3}, // ID3-format metadata 616 {[]byte{'I', 'D', '3', 4}, mp3}, // ID3-format metadata 617 {[]byte{'I', 'I', '*', 000}, tiff}, 618 }, // 73 I 619 nil, // 74 J 620 nil, // 75 K 621 nil, // 76 L 622 { 623 {[]byte{'M', 'M', 000, '*'}, tiff}, 624 {[]byte{'M', 'T', 'h', 'd'}, mid}, 625 {[]byte{'M', 'Z', cba, 000, cba, 000}, exe}, 626 // {[]byte{'M', 'Z', 0x90, 000, 003, 000}, exe}, 627 // {[]byte{'M', 'Z', 0x78, 000, 001, 000}, exe}, 628 // {[]byte{'M', 'Z', 'P', 000, 002, 000}, exe}, 629 }, // 77 M 630 nil, // 78 N 631 { 632 {[]byte{'O', 'g', 'g', 'S'}, ogg}, 633 }, // 79 O 634 { 635 {[]byte{'P', 'K', 003, 004}, zip}, 636 }, // 80 P 637 nil, // 81 Q 638 { 639 {[]byte{'R', 'I', 'F', 'F', cba, cba, cba, cba, 'W', 'E', 'B', 'P'}, webp}, 640 {[]byte{'R', 'I', 'F', 'F', cba, cba, cba, cba, 'W', 'A', 'V', 'E'}, wav}, 641 {[]byte{'R', 'I', 'F', 'F', cba, cba, cba, cba, 'A', 'V', 'I', ' '}, avi}, 642 }, // 82 R 643 { 644 {sqlite3db, sqlite3}, 645 }, // 83 S 646 nil, // 84 T 647 nil, // 85 U 648 nil, // 86 V 649 nil, // 87 W 650 nil, // 88 X 651 nil, // 89 Y 652 nil, // 90 Z 653 nil, // 91 [ 654 nil, // 92 \ 655 nil, // 93 ] 656 nil, // 94 ^ 657 nil, // 95 _ 658 nil, // 96 ` 659 nil, // 97 a 660 nil, // 98 b 661 { 662 {[]byte{'c', 'a', 'f', 'f', 000, 001, 000, 000}, caf}, 663 }, // 99 c 664 nil, // 100 d 665 nil, // 101 e 666 { 667 {[]byte{'f', 'L', 'a', 'C'}, flac}, 668 }, // 102 f 669 nil, // 103 g 670 nil, // 104 h 671 nil, // 105 i 672 nil, // 106 j 673 nil, // 107 k 674 nil, // 108 l 675 nil, // 109 m 676 nil, // 110 n 677 nil, // 111 o 678 nil, // 112 p 679 nil, // 113 q 680 nil, // 114 r 681 nil, // 115 s 682 nil, // 116 t 683 nil, // 117 u 684 nil, // 118 v 685 nil, // 119 w 686 nil, // 120 x 687 nil, // 121 y 688 nil, // 122 z 689 { 690 {[]byte{'{', '\\', 'r', 't', 'f'}, rtf}, 691 }, // 123 { 692 nil, // 124 | 693 nil, // 125 } 694 nil, // 126 695 { 696 {[]byte{127, 'E', 'L', 'F'}, elf}, 697 }, // 127 698 nil, // 128 699 nil, // 129 700 nil, // 130 701 nil, // 131 702 nil, // 132 703 nil, // 133 704 nil, // 134 705 nil, // 135 706 nil, // 136 707 { 708 {[]byte{0x89, 'P', 'N', 'G', 0x0D, 0x0A, 0x1A, 0x0A}, png}, 709 }, // 137 710 nil, // 138 711 nil, // 139 712 nil, // 140 713 nil, // 141 714 nil, // 142 715 nil, // 143 716 nil, // 144 717 nil, // 145 718 nil, // 146 719 nil, // 147 720 nil, // 148 721 nil, // 149 722 nil, // 150 723 nil, // 151 724 nil, // 152 725 nil, // 153 726 nil, // 154 727 nil, // 155 728 nil, // 156 729 nil, // 157 730 nil, // 158 731 nil, // 159 732 nil, // 160 733 nil, // 161 734 nil, // 162 735 nil, // 163 736 nil, // 164 737 nil, // 165 738 nil, // 166 739 nil, // 167 740 nil, // 168 741 nil, // 169 742 nil, // 170 743 nil, // 171 744 nil, // 172 745 nil, // 173 746 nil, // 174 747 nil, // 175 748 nil, // 176 749 nil, // 177 750 nil, // 178 751 nil, // 179 752 nil, // 180 753 nil, // 181 754 nil, // 182 755 nil, // 183 756 nil, // 184 757 nil, // 185 758 nil, // 186 759 nil, // 187 760 nil, // 188 761 nil, // 189 762 nil, // 190 763 nil, // 191 764 nil, // 192 765 nil, // 193 766 nil, // 194 767 nil, // 195 768 nil, // 196 769 nil, // 197 770 nil, // 198 771 nil, // 199 772 nil, // 200 773 nil, // 201 774 nil, // 202 775 nil, // 203 776 nil, // 204 777 nil, // 205 778 nil, // 206 779 nil, // 207 780 nil, // 208 781 nil, // 209 782 nil, // 210 783 nil, // 211 784 nil, // 212 785 nil, // 213 786 nil, // 214 787 nil, // 215 788 nil, // 216 789 nil, // 217 790 nil, // 218 791 nil, // 219 792 nil, // 220 793 nil, // 221 794 nil, // 222 795 nil, // 223 796 nil, // 224 797 nil, // 225 798 nil, // 226 799 nil, // 227 800 nil, // 228 801 nil, // 229 802 nil, // 230 803 nil, // 231 804 nil, // 232 805 nil, // 233 806 nil, // 234 807 nil, // 235 808 nil, // 236 809 nil, // 237 810 nil, // 238 811 nil, // 239 812 nil, // 240 813 nil, // 241 814 nil, // 242 815 nil, // 243 816 nil, // 244 817 nil, // 245 818 nil, // 246 819 nil, // 247 820 nil, // 248 821 nil, // 249 822 nil, // 250 823 nil, // 251 824 nil, // 252 825 nil, // 253 826 nil, // 254 827 { 828 {[]byte{0xFF, 0xD8, 0xFF}, jpg}, 829 {[]byte{0xFF, 0xF3, 0x48, 0xC4, 0x00}, mp3}, 830 {[]byte{0xFF, 0xFB}, mp3}, 831 }, // 255 832 }