Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec4961ae50 |
@@ -63,15 +63,12 @@ func Parse(r io.Reader) ([]Chunk, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copied rather than allocated up front, as ID3Chunk does: the
|
data := make([]byte, size)
|
||||||
// size is four bytes off the file, so a truncated one is free to
|
if _, err := io.ReadFull(r, data); err != nil {
|
||||||
// declare a chunk larger than the whole of itself.
|
|
||||||
var data bytes.Buffer
|
|
||||||
if _, err := io.CopyN(&data, r, int64(size)); err != nil {
|
|
||||||
return nil, fmt.Errorf("read chunk data for %q: %w", id, err)
|
return nil, fmt.Errorf("read chunk data for %q: %w", id, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
chunks = append(chunks, Chunk{ID: id, Data: data.Bytes()})
|
chunks = append(chunks, Chunk{ID: id, Data: data})
|
||||||
|
|
||||||
// Odd-length chunks have a padding byte. Lenient: if the
|
// Odd-length chunks have a padding byte. Lenient: if the
|
||||||
// read fails (e.g. EOF), just break rather than error.
|
// read fails (e.g. EOF), just break rather than error.
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"runtime"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"yellowjacket/backend/riff"
|
"yellowjacket/backend/riff"
|
||||||
@@ -210,45 +209,3 @@ func TestParse_ReadsEveryChunkInOrder(t *testing.T) {
|
|||||||
t.Errorf("odd chunk data: got %q, want %q", chunks[1].Data, "INFOodd")
|
t.Errorf("odd chunk data: got %q, want %q", chunks[1].Data, "INFOodd")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A chunk size is four bytes read off the file, so a truncated or
|
|
||||||
// malformed WAV is free to declare a chunk larger than the whole of
|
|
||||||
// itself. Parse must grow with what arrives rather than with what was
|
|
||||||
// claimed.
|
|
||||||
//
|
|
||||||
// This measures the allocation instead of the error because the error
|
|
||||||
// is the same either way: a build sizing its buffer from the header
|
|
||||||
// reports the truncation correctly, having asked the allocator for a
|
|
||||||
// gigabyte on the way. Deliberately not parallel — TotalAlloc is
|
|
||||||
// process-wide, and a test paused beside another one is measuring it
|
|
||||||
// too.
|
|
||||||
func TestParse_DoesNotAllocateWhatAChunkClaims(t *testing.T) {
|
|
||||||
// Large enough that a header-sized buffer is unmistakable, in a
|
|
||||||
// container of a few dozen bytes.
|
|
||||||
const declared = 1 << 30
|
|
||||||
|
|
||||||
var raw bytes.Buffer
|
|
||||||
|
|
||||||
raw.WriteString("RIFF")
|
|
||||||
_ = binary.Write(&raw, binary.LittleEndian, uint32(declared+12))
|
|
||||||
raw.WriteString("WAVE")
|
|
||||||
raw.WriteString("data")
|
|
||||||
_ = binary.Write(&raw, binary.LittleEndian, uint32(declared))
|
|
||||||
raw.WriteString("and then the file ends")
|
|
||||||
|
|
||||||
var before, after runtime.MemStats
|
|
||||||
|
|
||||||
runtime.GC()
|
|
||||||
runtime.ReadMemStats(&before)
|
|
||||||
|
|
||||||
if _, err := riff.Parse(bytes.NewReader(raw.Bytes())); err == nil {
|
|
||||||
t.Fatal("Parse: got nil error for a chunk larger than the file holding it")
|
|
||||||
}
|
|
||||||
|
|
||||||
runtime.ReadMemStats(&after)
|
|
||||||
|
|
||||||
if grew := after.TotalAlloc - before.TotalAlloc; grew > 1<<20 {
|
|
||||||
t.Errorf("Parse allocated %d bytes reading a %d-byte file whose chunk header claimed %d",
|
|
||||||
grew, raw.Len(), declared)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -103,12 +103,17 @@ async function queueSixAndOpen(app: Page): Promise<void> {
|
|||||||
*
|
*
|
||||||
* `explore-link` routes a track name to its *album's* page, so a
|
* `explore-link` routes a track name to its *album's* page, so a
|
||||||
* track with no album renders a name that navigates nowhere — and
|
* track with no album renders a name that navigates nowhere — and
|
||||||
* the fixture library deliberately contains two (`01 Tone A`,
|
* the fixture library deliberately contains two,
|
||||||
* `02 Tone B`). Which tracks arrive first is `audio_files.id`
|
* `unsorted/no-tags-at-all.mp3` and `unsorted/title-only.mp3`.
|
||||||
* order, i.e. the order the **scan** inserted them, which depends
|
* (It contained four until #104: the two WAVs under `Field
|
||||||
* on concurrency and directory traversal: locally the first eight
|
* Recordings/Test Tones` had been tagged on disk all along and
|
||||||
* all had albums and the spec passed twice over, and CI rebuilds
|
* scan in with their album now, so they are ordinary tracks and
|
||||||
* its seed with a real scan and got a different eight.
|
* not examples of this.) Which tracks arrive first is
|
||||||
|
* `audio_files.id` order, i.e. the order the **scan** inserted
|
||||||
|
* them, which depends on concurrency and directory traversal:
|
||||||
|
* locally the first eight all had albums and the spec passed twice
|
||||||
|
* over, and CI rebuilds its seed with a real scan and got a
|
||||||
|
* different eight.
|
||||||
*
|
*
|
||||||
* Asking for what the test needs is the fix. It is not a
|
* Asking for what the test needs is the fix. It is not a
|
||||||
* narrowing: every assertion here wants an ordinary track, and
|
* narrowing: every assertion here wants an ordinary track, and
|
||||||
|
|||||||
Reference in New Issue
Block a user