| 814 | } |
| 815 | |
| 816 | function createUtf8Validator(): { write(chunk: Buffer): void; end(): void } { |
| 817 | let needed = 0; |
| 818 | let lower = 0x80; |
| 819 | let upper = 0xbf; |
| 820 | |
| 821 | const fail = (): never => { |
| 822 | throw new TypeError('Invalid UTF-8 data'); |
| 823 | }; |
| 824 | |
| 825 | return { |
| 826 | write(chunk: Buffer): void { |
| 827 | for (let i = 0; i < chunk.length; i += 1) { |
| 828 | const byte = chunk[i]; |
| 829 | if (byte === undefined) continue; |
| 830 | if (needed === 0) { |
| 831 | if (byte <= 0x7f) continue; |
| 832 | if (byte >= 0xc2 && byte <= 0xdf) { |
| 833 | needed = 1; |
| 834 | } else if (byte === 0xe0) { |
| 835 | needed = 2; |
| 836 | lower = 0xa0; |
| 837 | } else if (byte >= 0xe1 && byte <= 0xec) { |
| 838 | needed = 2; |
| 839 | } else if (byte === 0xed) { |
| 840 | needed = 2; |
| 841 | upper = 0x9f; |
| 842 | } else if (byte >= 0xee && byte <= 0xef) { |
| 843 | needed = 2; |
| 844 | } else if (byte === 0xf0) { |
| 845 | needed = 3; |
| 846 | lower = 0x90; |
| 847 | } else if (byte >= 0xf1 && byte <= 0xf3) { |
| 848 | needed = 3; |
| 849 | } else if (byte === 0xf4) { |
| 850 | needed = 3; |
| 851 | upper = 0x8f; |
| 852 | } else { |
| 853 | fail(); |
| 854 | } |
| 855 | } else { |
| 856 | if (byte < lower || byte > upper) fail(); |
| 857 | lower = 0x80; |
| 858 | upper = 0xbf; |
| 859 | needed -= 1; |
| 860 | } |
| 861 | } |
| 862 | }, |
| 863 | end(): void { |
| 864 | if (needed !== 0) fail(); |
| 865 | }, |
| 866 | }; |
| 867 | } |
| 868 | |
| 869 | async function readRange( |
| 870 | fh: Awaited<ReturnType<typeof open>>, |