No global state for easier testability
This commit is contained in:
+27
-13
@@ -29,25 +29,44 @@ AssetIndex :: struct #packed #all_or_none {
|
|||||||
offset: u64,
|
offset: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
Offsets :: struct {
|
AssetTable :: struct {
|
||||||
asset_index_offset: u64,
|
asset_index_offset: u64,
|
||||||
data_offset: u64,
|
data_offset: u64,
|
||||||
asset_offsets: [dynamic]u64,
|
asset_offsets: [dynamic]u64,
|
||||||
asset_sizes: [dynamic]u64,
|
asset_sizes: [dynamic]u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------
|
Writer :: struct {
|
||||||
|
chunk_file: ^os.File,
|
||||||
@(private)
|
asset_file: ^os.File,
|
||||||
offsets: Offsets
|
asset_table: AssetTable,
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
compute_metadata_offsets :: proc(number_of_assets: u64) {
|
writer_init :: proc(
|
||||||
|
number_of_assets: u64,
|
||||||
|
allocator := context.allocator,
|
||||||
|
) -> (
|
||||||
|
w: Writer,
|
||||||
|
err: runtime.Allocator_Error,
|
||||||
|
) #optional_allocator_error {
|
||||||
// [ Header ][ []Asset Index ][ Data ]
|
// [ Header ][ []Asset Index ][ Data ]
|
||||||
offsets.asset_index_offset = size_of(Header)
|
w.asset_table.asset_index_offset = size_of(Header)
|
||||||
offsets.data_offset =
|
w.asset_table.data_offset =
|
||||||
size_of(Header) + (size_of(AssetIndex) * number_of_assets)
|
size_of(Header) + (size_of(AssetIndex) * number_of_assets)
|
||||||
|
w.asset_table.asset_offsets = make([dynamic]u64, allocator) or_return
|
||||||
|
w.asset_table.asset_sizes = make([dynamic]u64, allocator) or_return
|
||||||
|
resize(&w.asset_table.asset_offsets, number_of_assets)
|
||||||
|
resize(&w.asset_table.asset_sizes, number_of_assets)
|
||||||
|
return w, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
writer_destroy :: proc(w: ^Writer) {
|
||||||
|
if w.chunk_file != nil do os.close(w.chunk_file)
|
||||||
|
if w.asset_file != nil do os.close(w.asset_file)
|
||||||
|
delete(w.asset_table.asset_offsets)
|
||||||
|
delete(w.asset_table.asset_sizes)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
@@ -71,8 +90,3 @@ compute_chunk_path :: proc(
|
|||||||
|
|
||||||
return chunk_path // owning string
|
return chunk_path // owning string
|
||||||
}
|
}
|
||||||
|
|
||||||
delete_offsets :: proc() {
|
|
||||||
delete(offsets.asset_offsets)
|
|
||||||
delete(offsets.asset_sizes)
|
|
||||||
}
|
|
||||||
|
|||||||
+31
-37
@@ -7,15 +7,8 @@ import "core:os"
|
|||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
@(private = "file")
|
|
||||||
chunk_file: ^os.File
|
|
||||||
|
|
||||||
@(private = "file")
|
|
||||||
asset_file: ^os.File
|
|
||||||
|
|
||||||
// -----------------------------------------
|
|
||||||
|
|
||||||
write_assets :: proc(
|
write_assets :: proc(
|
||||||
|
w: ^Writer,
|
||||||
output: ^os.File,
|
output: ^os.File,
|
||||||
entries: []FileEntry,
|
entries: []FileEntry,
|
||||||
size_per_chunk: u64 = SIZE_PER_CHUNK,
|
size_per_chunk: u64 = SIZE_PER_CHUNK,
|
||||||
@@ -27,15 +20,13 @@ write_assets :: proc(
|
|||||||
asset_index := 0
|
asset_index := 0
|
||||||
chunk_offset: u64 = 0
|
chunk_offset: u64 = 0
|
||||||
asset_offset: u64 = 0
|
asset_offset: u64 = 0
|
||||||
total_offset: u64 = offsets.data_offset
|
total_offset: u64 = w.asset_table.data_offset
|
||||||
number_of_assets := len(entries)
|
number_of_assets := len(entries)
|
||||||
resize(&offsets.asset_offsets, number_of_assets)
|
|
||||||
resize(&offsets.asset_sizes, number_of_assets)
|
|
||||||
|
|
||||||
if size_per_chunk < offsets.data_offset {
|
if size_per_chunk < w.asset_table.data_offset {
|
||||||
fmt.eprintln(
|
fmt.eprintln(
|
||||||
"error: chunk size too small to hold asset index:",
|
"error: chunk size too small to hold asset index:",
|
||||||
offsets.data_offset,
|
w.asset_table.data_offset,
|
||||||
)
|
)
|
||||||
os.exit(1)
|
os.exit(1)
|
||||||
}
|
}
|
||||||
@@ -43,25 +34,25 @@ write_assets :: proc(
|
|||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
// Create new chunk
|
// Create new chunk
|
||||||
chunk_stream := create_new_chunk(output_path, chunk_index, allocator)
|
chunk_stream := create_new_chunk(w, output_path, chunk_index, allocator)
|
||||||
|
|
||||||
// Edge case of the chunk size being exactly the size of the data offset
|
// Edge case of the chunk size being exactly the size of the data offset
|
||||||
if size_per_chunk == offsets.data_offset {
|
if size_per_chunk == w.asset_table.data_offset {
|
||||||
chunk_index += 1
|
chunk_index += 1
|
||||||
chunk_stream = create_new_chunk(output_path, chunk_index, allocator)
|
chunk_stream = create_new_chunk(w, output_path, chunk_index, allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chunk 0 starts after metadata
|
// Chunk 0 starts after metadata
|
||||||
if (chunk_index == 0) {
|
if (chunk_index == 0) {
|
||||||
chunk_offset = offsets.data_offset
|
chunk_offset = w.asset_table.data_offset
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
// Open asset
|
// Open asset
|
||||||
asset_stream, asset_size := open_asset(entries[asset_index].relpath)
|
asset_stream, asset_size := open_asset(w, entries[asset_index].relpath)
|
||||||
offsets.asset_offsets[asset_index] = total_offset
|
w.asset_table.asset_offsets[asset_index] = total_offset
|
||||||
offsets.asset_sizes[asset_index] = asset_size
|
w.asset_table.asset_sizes[asset_index] = asset_size
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
@@ -83,9 +74,12 @@ write_assets :: proc(
|
|||||||
asset_index += 1
|
asset_index += 1
|
||||||
asset_offset = 0
|
asset_offset = 0
|
||||||
if asset_index == number_of_assets do break
|
if asset_index == number_of_assets do break
|
||||||
asset_stream, asset_size = open_asset(entries[asset_index].relpath)
|
asset_stream, asset_size = open_asset(
|
||||||
offsets.asset_offsets[asset_index] = total_offset
|
w,
|
||||||
offsets.asset_sizes[asset_index] = asset_size
|
entries[asset_index].relpath,
|
||||||
|
)
|
||||||
|
w.asset_table.asset_offsets[asset_index] = total_offset
|
||||||
|
w.asset_table.asset_sizes[asset_index] = asset_size
|
||||||
|
|
||||||
} else if asset_remaining == chunk_remaining {
|
} else if asset_remaining == chunk_remaining {
|
||||||
|
|
||||||
@@ -97,14 +91,18 @@ write_assets :: proc(
|
|||||||
asset_index += 1
|
asset_index += 1
|
||||||
asset_offset = 0
|
asset_offset = 0
|
||||||
if asset_index == number_of_assets do break
|
if asset_index == number_of_assets do break
|
||||||
asset_stream, asset_size = open_asset(entries[asset_index].relpath)
|
asset_stream, asset_size = open_asset(
|
||||||
offsets.asset_offsets[asset_index] = total_offset
|
w,
|
||||||
offsets.asset_sizes[asset_index] = asset_size
|
entries[asset_index].relpath,
|
||||||
|
)
|
||||||
|
w.asset_table.asset_offsets[asset_index] = total_offset
|
||||||
|
w.asset_table.asset_sizes[asset_index] = asset_size
|
||||||
|
|
||||||
// Create new chunk
|
// Create new chunk
|
||||||
chunk_index += 1
|
chunk_index += 1
|
||||||
chunk_offset = 0
|
chunk_offset = 0
|
||||||
chunk_stream = create_new_chunk(
|
chunk_stream = create_new_chunk(
|
||||||
|
w,
|
||||||
output_path,
|
output_path,
|
||||||
chunk_index,
|
chunk_index,
|
||||||
allocator,
|
allocator,
|
||||||
@@ -118,22 +116,18 @@ write_assets :: proc(
|
|||||||
chunk_index += 1
|
chunk_index += 1
|
||||||
chunk_offset = 0
|
chunk_offset = 0
|
||||||
chunk_stream = create_new_chunk(
|
chunk_stream = create_new_chunk(
|
||||||
|
w,
|
||||||
output_path,
|
output_path,
|
||||||
chunk_index,
|
chunk_index,
|
||||||
allocator,
|
allocator,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------
|
|
||||||
|
|
||||||
// Cleaup leftover
|
|
||||||
os.close(chunk_file)
|
|
||||||
os.close(asset_file)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
create_new_chunk :: proc(
|
create_new_chunk :: proc(
|
||||||
|
w: ^Writer,
|
||||||
output_path: string,
|
output_path: string,
|
||||||
chunk_index: int,
|
chunk_index: int,
|
||||||
allocator: runtime.Allocator,
|
allocator: runtime.Allocator,
|
||||||
@@ -149,24 +143,24 @@ create_new_chunk :: proc(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if chunk_file != nil do os.close(chunk_file) // close previous
|
if w.chunk_file != nil do os.close(w.chunk_file) // close previous
|
||||||
|
|
||||||
// Create new chunk
|
// Create new chunk
|
||||||
cf_err: os.Error
|
cf_err: os.Error
|
||||||
chunk_file, cf_err = os.create(chunk_path)
|
w.chunk_file, cf_err = os.create(chunk_path)
|
||||||
if cf_err != nil {
|
if cf_err != nil {
|
||||||
fmt.eprintln("error: create new chunk failed:", cf_err)
|
fmt.eprintln("error: create new chunk failed:", cf_err)
|
||||||
os.exit(1)
|
os.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
chunk_stream := os.to_stream(chunk_file)
|
chunk_stream := os.to_stream(w.chunk_file)
|
||||||
|
|
||||||
return chunk_stream
|
return chunk_stream
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
open_asset :: proc(path: string) -> (io.Stream, u64) {
|
open_asset :: proc(w: ^Writer, path: string) -> (io.Stream, u64) {
|
||||||
if asset_file != nil do os.close(asset_file) // close previous
|
if w.asset_file != nil do os.close(w.asset_file) // close previous
|
||||||
|
|
||||||
// Open asset
|
// Open asset
|
||||||
asset, open_err := os.open(path, {.Read})
|
asset, open_err := os.open(path, {.Read})
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import "core:os"
|
|||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
write_metadata :: proc(
|
write_metadata :: proc(
|
||||||
|
w: ^Writer,
|
||||||
output: ^os.File,
|
output: ^os.File,
|
||||||
entries: []FileEntry,
|
entries: []FileEntry,
|
||||||
size_per_chunk: u64 = SIZE_PER_CHUNK,
|
size_per_chunk: u64 = SIZE_PER_CHUNK,
|
||||||
@@ -28,20 +29,22 @@ write_metadata :: proc(
|
|||||||
}
|
}
|
||||||
defer os.close(chunk_file)
|
defer os.close(chunk_file)
|
||||||
|
|
||||||
write_header(chunk_file, number_of_assets, size_per_chunk, compression)
|
write_header(w, chunk_file, number_of_assets, size_per_chunk, compression)
|
||||||
|
|
||||||
write_asset_index(chunk_file, entries, number_of_assets, allocator)
|
write_asset_index(w, chunk_file, entries, number_of_assets, allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
write_header :: proc(
|
write_header :: proc(
|
||||||
|
w: ^Writer,
|
||||||
chunk_file: ^os.File,
|
chunk_file: ^os.File,
|
||||||
number_of_assets: u32,
|
number_of_assets: u32,
|
||||||
size_per_chunk: u64 = SIZE_PER_CHUNK,
|
size_per_chunk: u64 = SIZE_PER_CHUNK,
|
||||||
compression: u16 = COMPRESSION,
|
compression: u16 = COMPRESSION,
|
||||||
) {
|
) {
|
||||||
last_offset := offsets.asset_offsets[len(offsets.asset_offsets) - 1]
|
last_offset :=
|
||||||
last_size := offsets.asset_sizes[len(offsets.asset_sizes) - 1]
|
w.asset_table.asset_offsets[len(w.asset_table.asset_offsets) - 1]
|
||||||
|
last_size := w.asset_table.asset_sizes[len(w.asset_table.asset_sizes) - 1]
|
||||||
total_size := last_offset + last_size
|
total_size := last_offset + last_size
|
||||||
|
|
||||||
header := Header {
|
header := Header {
|
||||||
@@ -64,6 +67,7 @@ write_header :: proc(
|
|||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
write_asset_index :: proc(
|
write_asset_index :: proc(
|
||||||
|
w: ^Writer,
|
||||||
chunk_file: ^os.File,
|
chunk_file: ^os.File,
|
||||||
entries: []FileEntry,
|
entries: []FileEntry,
|
||||||
number_of_assets: u32,
|
number_of_assets: u32,
|
||||||
@@ -90,8 +94,8 @@ write_asset_index :: proc(
|
|||||||
|
|
||||||
ai := cast(^AssetIndex)offset_pointer
|
ai := cast(^AssetIndex)offset_pointer
|
||||||
copy(ai.relpath[:PATH_SIZE], f.relpath)
|
copy(ai.relpath[:PATH_SIZE], f.relpath)
|
||||||
ai.size = offsets.asset_sizes[i]
|
ai.size = w.asset_table.asset_sizes[i]
|
||||||
ai.offset = offsets.asset_offsets[i]
|
ai.offset = w.asset_table.asset_offsets[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(len(index_bytes) % size_of(AssetIndex) == 0) // clean multiple
|
assert(len(index_bytes) % size_of(AssetIndex) == 0) // clean multiple
|
||||||
@@ -100,7 +104,7 @@ write_asset_index :: proc(
|
|||||||
n, err := os.write_at(
|
n, err := os.write_at(
|
||||||
chunk_file,
|
chunk_file,
|
||||||
index_bytes,
|
index_bytes,
|
||||||
cast(i64)offsets.asset_index_offset,
|
cast(i64)w.asset_table.asset_index_offset,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.eprintln("error: chunk write error:", err)
|
fmt.eprintln("error: chunk write error:", err)
|
||||||
|
|||||||
+12
-4
@@ -20,8 +20,16 @@ main :: proc() {
|
|||||||
entries := file.list_dir_recursive(opts.input)
|
entries := file.list_dir_recursive(opts.input)
|
||||||
defer file.delete_entries(&entries)
|
defer file.delete_entries(&entries)
|
||||||
|
|
||||||
file.compute_metadata_offsets(cast(u64)len(entries))
|
w := file.writer_init(cast(u64)len(entries))
|
||||||
file.write_assets(opts.output, entries[:], opts.size, opts.compression)
|
defer file.writer_destroy(&w)
|
||||||
file.write_metadata(opts.output, entries[:], opts.size, opts.compression)
|
|
||||||
file.delete_offsets()
|
file.write_assets(&w, opts.output, entries[:], opts.size, opts.compression)
|
||||||
|
|
||||||
|
file.write_metadata(
|
||||||
|
&w,
|
||||||
|
opts.output,
|
||||||
|
entries[:],
|
||||||
|
opts.size,
|
||||||
|
opts.compression,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user