Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Bolt's Journal

## 2024-05-22 - [Buffer Allocation in Hot Loops]
**Learning:** In Go, repeatedly allocating buffers inside a loop (especially in I/O intensive code) generates significant GC pressure.
**Action:** Use `sync.Pool` or pre-allocate buffers outside the loop when possible.

## 2024-05-22 - [Reflection in Binary Reading]
**Learning:** `binary.Read` uses reflection which is slow for simple types in hot paths.
**Action:** Use `io.ReadFull` with a byte slice and `binary.LittleEndian.UintXX` (or BigEndian) for primitive types in performance-critical sections.
52 changes: 31 additions & 21 deletions pkg/dicomio/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ type Reader struct {
// particular encoding.Decoder within this CodingSystem is nil, assume
// UTF-8.
cs charset.CodingSystem
// scratch is a buffer used for small reads to avoid allocating new slices
// for every read operation (which binary.Read would otherwise do via reflection).
scratch [8]byte
}

// NewReader creates and returns a new *dicomio.Reader.
Expand Down Expand Up @@ -77,51 +80,58 @@ func (r *Reader) Read(p []byte) (int, error) {

// ReadUInt8 reads an uint8 from the underlying *Reader.
func (r *Reader) ReadUInt8() (uint8, error) {
var out uint8
err := binary.Read(r, r.bo, &out)
return out, err
if _, err := io.ReadFull(r, r.scratch[:1]); err != nil {
return 0, err
}
return r.scratch[0], nil
}

// ReadUInt16 reads an uint16 from the underlying *Reader.
func (r *Reader) ReadUInt16() (uint16, error) {
var out uint16
err := binary.Read(r, r.bo, &out)
return out, err
if _, err := io.ReadFull(r, r.scratch[:2]); err != nil {
return 0, err
}
return r.bo.Uint16(r.scratch[:2]), nil
}

// ReadUInt32 reads an uint32 from the underlying *Reader.
func (r *Reader) ReadUInt32() (uint32, error) {
var out uint32
err := binary.Read(r, r.bo, &out)
return out, err
if _, err := io.ReadFull(r, r.scratch[:4]); err != nil {
return 0, err
}
return r.bo.Uint32(r.scratch[:4]), nil
}

// ReadInt16 reads an int16 from the underlying *Reader.
func (r *Reader) ReadInt16() (int16, error) {
var out int16
err := binary.Read(r, r.bo, &out)
return out, err
if _, err := io.ReadFull(r, r.scratch[:2]); err != nil {
return 0, err
}
return int16(r.bo.Uint16(r.scratch[:2])), nil
}

// ReadInt32 reads an int32 from the underlying *Reader.
func (r *Reader) ReadInt32() (int32, error) {
var out int32
err := binary.Read(r, r.bo, &out)
return out, err
if _, err := io.ReadFull(r, r.scratch[:4]); err != nil {
return 0, err
}
return int32(r.bo.Uint32(r.scratch[:4])), nil
}

// ReadFloat32 reads a float32 from the underlying *Reader.
func (r *Reader) ReadFloat32() (float32, error) {
var out float32
err := binary.Read(r, r.bo, &out)
return out, err
if _, err := io.ReadFull(r, r.scratch[:4]); err != nil {
return 0, err
}
return math.Float32frombits(r.bo.Uint32(r.scratch[:4])), nil
}

// ReadFloat64 reads a float64 from the underlying *Reader.
func (r *Reader) ReadFloat64() (float64, error) {
var out float64
err := binary.Read(r, r.bo, &out)
return out, err
if _, err := io.ReadFull(r, r.scratch[:8]); err != nil {
return 0, err
}
return math.Float64frombits(r.bo.Uint64(r.scratch[:8])), nil
}

func internalReadString(data []byte, d *encoding.Decoder) (string, error) {
Expand Down