BakedFileSystem::BakedFile
BakedFile represents a virtual file in a BakedFileSystem.
BakedFile is a read-only IO wrapper around files embedded at compile-time.
Write operations will raise ReadOnlyError since embedded files cannot be
modified at runtime.
Architecture
Files are stored compressed in the binary's read-only data section as byte slices. On first access, BakedFile creates:
- Memory IO: Wraps the compressed byte slice
- Gzip Reader: Wraps the memory IO for transparent decompression
- Wrapped IO: Either the memory IO (for .gz files) or gzip reader
This lazy-loading approach minimizes memory usage:
- Compressed data stays in read-only binary section
- Decompression happens on-demand during read
- No heap allocation unless content is explicitly stored
Streaming Behavior
BakedFile is a forward-only stream by default:
- Read operations consume the stream
- Call
rewindto return to the beginning - Each
rewindrecreates the decompression reader (see#rewindfor details)
Thread Safety
Each call to get() or get?() returns a new BakedFile instance with
independent state, making concurrent access safe.
Usage
file = MyFileSystem.get("hello-world.txt")
file.path # => "hello-world.txt"
file.size # => 12
file.gets_to_end # => "Hello World\n"
file.compressed? # => false
Constructors
Instance methods
Closes the file and releases resources. Can be called multiple times safely. After closing, read operations will raise IO::Error.
Returns true if the file has been closed.
Returns whether this file is compressed. If not, it is decompressed on read.
Returns the compressed size of this virtual file.
See #size for the real size of the (uncompressed) file.
Ensures resources are freed when the object is garbage collected.
Returns the path in the virtual file system.
Reads at most slice.size bytes from this IO into slice.
Returns the number of bytes read, which is 0 if and only if there is no
more data to read (so checking for 0 is the way to detect end of file).
io = IO::Memory.new "hello"
slice = Bytes.new(4)
io.read(slice) # => 4
slice # => Bytes[104, 101, 108, 108]
io.read(slice) # => 1
slice # => Bytes[111, 101, 108, 108]
io.read(slice) # => 0
Return the data for this file as a String.
DEPRECATED: BakedFile can be used as an IO directly. Use gets_to_end instead
Rewinds the file to the beginning for re-reading.
Implementation Note
This method recreates the gzip decompression reader instead of rewinding it
because Compress::Gzip::Reader is a forward-only stream that doesn't support
seeking backward. This is intentional and necessary for correct behavior.
Why Recreation is Required
Compress::Gzip::Readermaintains internal state during decompression- This state cannot be reset to return to the beginning
- Creating a new reader over the rewound underlying stream is the only way to re-read
Performance Implications
- Creating a new reader is fast (no decompression happens yet)
- Decompression happens on-demand during read operations
- Memory usage is minimal (same underlying byte slice is reused)
- This is the standard approach for streaming decompression
Alternatives Considered
Cache decompressed content:
- Pro: True rewind without recreation
- Con: Significant memory overhead (defeats purpose of streaming)
- Con: Not suitable for large files
- Decision: Rejected
Add seeking to Gzip::Reader:
- Pro: More intuitive API
- Con: Requires changes to Crystal standard library
- Con: Decompression algorithms are inherently forward-only
- Decision: Not feasible
Returns the size of this virtual file.
Return the data for this file as a URL-safe Base64-encoded String.
DEPRECATED: BakedFile can be used as an IO directly.
Return the file's data as a Slice(UInt8)
DEPRECATED: BakedFile can be used as an IO directly.
Returns a Bytes holding the (compressed) content of this virtual file.
This data needs to be extracted using a Compress::Gzip::Reader unless #compressed? is true.
Writes the contents of slice into this IO.
io = IO::Memory.new
slice = Bytes.new(4) { |i| ('a'.ord + i).to_u8 }
io.write(slice)
io.to_s # => "abcd"
Write the file's data to the given IO, minimizing any memory copies or unnecessary conversions.
DEPRECATED: BakedFile can be used as an IO directly.