|
| 1 | +package compression |
| 2 | + |
| 3 | +import ( |
| 4 | + "bytes" |
| 5 | + "errors" |
| 6 | + "io" |
| 7 | + "testing" |
| 8 | +) |
| 9 | + |
| 10 | +type memorySparseFile struct { |
| 11 | + buffer bytes.Buffer |
| 12 | + pos int64 |
| 13 | +} |
| 14 | + |
| 15 | +func (m *memorySparseFile) Seek(offset int64, whence int) (int64, error) { |
| 16 | + var newPos int64 |
| 17 | + switch whence { |
| 18 | + case io.SeekStart: |
| 19 | + newPos = offset |
| 20 | + case io.SeekCurrent: |
| 21 | + newPos = m.pos + offset |
| 22 | + case io.SeekEnd: |
| 23 | + newPos = int64(m.buffer.Len()) + offset |
| 24 | + default: |
| 25 | + return 0, errors.New("unsupported seek whence") |
| 26 | + } |
| 27 | + |
| 28 | + if newPos < 0 { |
| 29 | + return 0, errors.New("negative position is not allowed") |
| 30 | + } |
| 31 | + |
| 32 | + m.pos = newPos |
| 33 | + return newPos, nil |
| 34 | +} |
| 35 | + |
| 36 | +func (m *memorySparseFile) Write(b []byte) (n int, err error) { |
| 37 | + if int64(m.buffer.Len()) < m.pos { |
| 38 | + padding := make([]byte, m.pos-int64(m.buffer.Len())) |
| 39 | + _, err := m.buffer.Write(padding) |
| 40 | + if err != nil { |
| 41 | + return 0, err |
| 42 | + } |
| 43 | + } |
| 44 | + |
| 45 | + m.buffer.Next(int(m.pos) - m.buffer.Len()) |
| 46 | + |
| 47 | + n, err = m.buffer.Write(b) |
| 48 | + m.pos += int64(n) |
| 49 | + return n, err |
| 50 | +} |
| 51 | + |
| 52 | +func (m *memorySparseFile) Close() error { |
| 53 | + return nil |
| 54 | +} |
| 55 | + |
| 56 | +func testInputWithWriteLen(t *testing.T, input []byte, chunkSize int) { |
| 57 | + m := &memorySparseFile{} |
| 58 | + sparseWriter := NewSparseWriter(m) |
| 59 | + |
| 60 | + for i := 0; i < len(input); i += chunkSize { |
| 61 | + end := i + chunkSize |
| 62 | + if end > len(input) { |
| 63 | + end = len(input) |
| 64 | + } |
| 65 | + _, err := sparseWriter.Write(input[i:end]) |
| 66 | + if err != nil { |
| 67 | + t.Fatalf("Expected no error, got %v", err) |
| 68 | + } |
| 69 | + } |
| 70 | + err := sparseWriter.Close() |
| 71 | + if err != nil { |
| 72 | + t.Fatalf("Expected no error, got %v", err) |
| 73 | + } |
| 74 | + if !bytes.Equal(input, m.buffer.Bytes()) { |
| 75 | + t.Fatalf("Incorrect output") |
| 76 | + } |
| 77 | +} |
| 78 | + |
| 79 | +func testInput(t *testing.T, inputBytes []byte) { |
| 80 | + currentLen := 1 |
| 81 | + for { |
| 82 | + testInputWithWriteLen(t, inputBytes, currentLen) |
| 83 | + currentLen <<= 1 |
| 84 | + if currentLen > len(inputBytes) { |
| 85 | + break |
| 86 | + } |
| 87 | + } |
| 88 | +} |
| 89 | + |
| 90 | +func TestSparseWriter(t *testing.T) { |
| 91 | + testInput(t, []byte("hello")) |
| 92 | + testInput(t, append(make([]byte, 100), []byte("hello")...)) |
| 93 | + testInput(t, []byte("")) |
| 94 | + |
| 95 | + // add "hello" at the beginning |
| 96 | + largeInput := make([]byte, 1024*1024) |
| 97 | + copy(largeInput, []byte("hello")) |
| 98 | + testInput(t, largeInput) |
| 99 | + |
| 100 | + // add "hello" at the end |
| 101 | + largeInput = make([]byte, 1024*1024) |
| 102 | + copy(largeInput[1024*1024-5:], []byte("hello")) |
| 103 | + testInput(t, largeInput) |
| 104 | + |
| 105 | + // add "hello" in the middle |
| 106 | + largeInput = make([]byte, 1024*1024) |
| 107 | + copy(largeInput[len(largeInput)/2:], []byte("hello")) |
| 108 | + testInput(t, largeInput) |
| 109 | +} |
0 commit comments