///|
fn write_coff(
builder : ArchiveBuilder,
metadata : WriteMetadata,
supplied : Array[WriteSymbol]?,
max_bytes : Int32,
max_members : Int32,
max_name_bytes : Int32,
max_symbols : Int32,
max_symbol_name_bytes : Int32,
) -> Result[Bytes, WriteError] {
try {
if max_bytes < 0 ||
max_members < 0 ||
max_name_bytes < 0 ||
max_symbols < 0 ||
max_symbol_name_bytes < 0 {
write_reject("WR003", None, "limits must be nonnegative")
}
let entries = builder.members
let symbols = supplied.unwrap_or([])
if entries.length() > 65535 {
write_reject(
"WR003",
None,
"COFF linker member ordinals support at most 65535 ordinary members",
)
}
if symbols.length() > max_symbols / 2 {
write_reject(
"WR003",
None,
"COFF symbol budget counts both linker members",
)
}
let mut records = write_add(
entries.length(),
2,
max_members,
None,
"archive entry limit exceeded",
)
let mut expanded_names = write_add(
0,
2,
max_name_bytes,
None,
"expanded entry-name byte limit exceeded",
)
let offsets : Map[Bytes, Int32] = Map([])
let long_names : Array[Bytes] = []
let mut long_size = 0
for i, entry in entries {
write_member_valid(entry, i)
expanded_names = write_add(
expanded_names,
entry.name.length(),
max_name_bytes,
Some(i),
"expanded entry-name byte limit exceeded",
)
if !write_gnu_short_name(entry.name) && offsets.get(entry.name) is None {
offsets[entry.name] = long_size
long_names.push(entry.name)
long_size = write_add(
long_size,
entry.name.length(),
max_bytes,
Some(i),
"archive byte limit exceeded",
)
long_size = write_add(
long_size,
1,
max_bytes,
Some(i),
"archive byte limit exceeded",
)
}
}
if long_size > 0 {
records = write_add(
records,
1,
max_members,
None,
"archive entry limit exceeded",
)
expanded_names = write_add(
expanded_names,
2,
max_name_bytes,
None,
"expanded entry-name byte limit exceeded",
)
}
ignore(records)
ignore(expanded_names)
let mut first_size = 4
let mut second_size = write_add(
8,
entries.length() * 4,
max_bytes,
None,
"archive byte limit exceeded",
)
let mut symbol_bytes = 0
for symbol in symbols {
if symbol.member_index < 0 || symbol.member_index >= entries.length() {
write_reject(
"WR005",
Some(symbol.member_index),
"symbol target is outside the ordinary members",
)
}
if symbol.name.is_empty() || symbol.name.find(b"\x00"[:]) is Some(_) {
write_reject(
"WR005",
Some(symbol.member_index),
"symbol names must be nonempty and contain no NUL",
)
}
for _copy in 0..<2 {
symbol_bytes = write_add(
symbol_bytes,
symbol.name.length(),
max_symbol_name_bytes,
Some(symbol.member_index),
"symbol-name byte limit exceeded",
)
}
first_size = write_add(
first_size,
5,
max_bytes,
None,
"archive byte limit exceeded",
)
first_size = write_add(
first_size,
symbol.name.length(),
max_bytes,
None,
"archive byte limit exceeded",
)
second_size = write_add(
second_size,
3,
max_bytes,
None,
"archive byte limit exceeded",
)
second_size = write_add(
second_size,
symbol.name.length(),
max_bytes,
None,
"archive byte limit exceeded",
)
}
ignore(symbol_bytes)
let mut end = write_add(
0,
8,
max_bytes,
None,
"archive byte limit exceeded",
)
end = write_record_end(end, first_size, max_bytes, None)
end = write_record_end(end, second_size, max_bytes, None)
if long_size > 0 {
end = write_record_end(end, long_size, max_bytes, None)
}
let headers : Array[Int32] = []
for i, entry in entries {
headers.push(end)
end = write_record_end(end, entry.data.length(), max_bytes, Some(i))
}
let ordered = Array::makei(symbols.length(), fn(i) { i })
ordered.sort_by(fn(a, b) {
let order = symbols[a].name.lexical_compare(symbols[b].name)
if order == 0 {
a - b
} else {
order
}
})
let out = @buffer.Buffer(size_hint=end)
out.write_bytes(b"!\n"[:])
let special : Metadata = {
timestamp: None,
uid: None,
gid: None,
mode: None,
}
write_header(out, b"/", special, first_size)
out.write_int_be(symbols.length())
for symbol in symbols {
out.write_int_be(headers[symbol.member_index])
}
for symbol in symbols {
out.write_bytes(symbol.name[:])
out.write_byte(b'\x00')
}
write_padding(out, first_size)
write_header(out, b"/", special, second_size)
out.write_int_le(entries.length())
for header in headers {
out.write_int_le(header)
}
out.write_int_le(symbols.length())
for i in ordered {
out.write_uint16_le((symbols[i].member_index + 1).to_uint16())
}
for i in ordered {
out.write_bytes(symbols[i].name[:])
out.write_byte(b'\x00')
}
write_padding(out, second_size)
if long_size > 0 {
write_header(out, b"//", special, long_size)
for name in long_names {
out.write_bytes(name[:])
out.write_byte(b'\x00')
}
write_padding(out, long_size)
}
let deterministic : Metadata = {
timestamp: Some(0L),
uid: Some(0L),
gid: Some(0L),
mode: Some(420L),
}
for entry in entries {
let name = if write_gnu_short_name(entry.name) {
entry.name + b"/"
} else {
@utf8.encode("/\{offsets[entry.name]}")
}
write_header(
out,
name,
if metadata == Deterministic {
deterministic
} else {
entry.metadata
},
entry.data.length(),
)
out.write_bytes(entry.data[:])
write_padding(out, entry.data.length())
}
Ok(out.to_bytes())
} catch {
InvalidWrite(error) => Err(error)
}
}