///|
pub struct Reader {
data : Bytes
header_ : Header
limits : Limits
index_start : Int32
index_size : Int32
features_start : Int32
mut cursor : Int32
mut ordinal : Int32
}
///|
pub fn Reader::open(
data : Bytes,
limits~ : Limits = Limits::default(),
) -> Reader raise FgbError {
limits.validate()
enforce(data.length(), limits.max_file_bytes, "file bytes")
validate_magic(data)
let header_size = size32(data, 8)
enforce(header_size, limits.max_header_bytes, "header bytes")
let index_start = checked_add(12, header_size)
let header = decode_header(
slice_bytes(data, 8, checked_add(header_size, 4)),
limits~,
)
let index_size = index_byte_length(
header.features_count,
header.index_node_size,
limits~,
)
let features_start = checked_add(index_start, index_size)
require_range(data, index_start, index_size)
{
data,
header_: header,
limits,
index_start,
index_size,
features_start,
cursor: features_start,
ordinal: 0,
}
}
///|
pub fn Reader::header(self : Reader) -> Header {
self.header_
}
///|
pub fn Reader::feature_section_offset(self : Reader) -> Int32 {
self.features_start
}
///|
pub fn Reader::has_index(self : Reader) -> Bool {
self.header_.index_node_size > 0
}
///|
pub fn Reader::rewind(self : Reader) -> Unit {
self.cursor = self.features_start
self.ordinal = 0
}
///|
fn Reader::record(
self : Reader,
offset : Int32,
ordinal : Int32,
) -> FeatureRecord raise FgbError {
let at = checked_add(self.features_start, offset)
let size = size32(self.data, at)
enforce(size, self.limits.max_feature_bytes, "feature bytes")
let byte_length = checked_add(size, 4)
let feature = decode_feature(
slice_bytes(self.data, at, byte_length),
self.header_,
limits=self.limits,
)
{ ordinal, offset, byte_length, feature, }
}
///|
pub fn Reader::next(self : Reader) -> FeatureRecord? raise FgbError {
if self.cursor == self.data.length() {
if self.header_.features_count > 0 &&
self.ordinal != self.header_.features_count {
raise InvalidData(self.cursor, "fewer features than header declares")
}
return None
}
if self.header_.features_count > 0 &&
self.ordinal >= self.header_.features_count {
raise InvalidData(self.cursor, "more features than header declares")
}
enforce(
checked_add(self.ordinal, 1),
self.limits.max_features,
"feature count",
)
let record = self.record(self.cursor - self.features_start, self.ordinal)
self.cursor = checked_add(self.cursor, record.byte_length)
self.ordinal += 1
Some(record)
}
///|
pub fn Reader::read_all(self : Reader) -> Dataset raise FgbError {
self.rewind()
let features : Array[Feature] = []
while self.next() is Some(record) {
features.push(record.feature)
}
{ header: self.header_, features, }
}
///|
pub fn read_dataset(
data : Bytes,
limits~ : Limits = Limits::default(),
) -> Dataset raise FgbError {
Reader::open(data, limits~).read_all()
}
///|
pub fn Reader::at(
self : Reader,
ordinal : Int32,
) -> FeatureRecord raise FgbError {
if ordinal < 0 {
raise InvalidArgument("negative feature ordinal")
}
if self.has_index() {
if ordinal >= self.header_.features_count {
raise InvalidArgument("feature ordinal exceeds count")
}
let levels = index_levels(
self.header_.features_count,
self.header_.index_node_size,
limits=self.limits,
)
let index = slice_bytes(self.data, self.index_start, self.index_size)
let node = decode_index_node(index, levels[0].start + ordinal)
return self.record(node.offset, ordinal)
}
let mut cursor = self.features_start
let mut current = 0
while cursor < self.data.length() {
let size = size32(self.data, cursor)
enforce(size, self.limits.max_feature_bytes, "feature bytes")
if current == ordinal {
return self.record(cursor - self.features_start, current)
}
cursor = checked_add(cursor, checked_add(size, 4))
current += 1
enforce(current, self.limits.max_features, "feature count")
}
raise InvalidArgument("feature ordinal exceeds count")
}
///|
pub fn Reader::query(
self : Reader,
bounds : Bounds,
) -> Array[FeatureRecord] raise FgbError {
bounds.validate()
let result : Array[FeatureRecord] = []
if self.has_index() {
let index = slice_bytes(self.data, self.index_start, self.index_size)
let hits = query_index(
index,
self.header_.features_count,
self.header_.index_node_size,
bounds,
limits=self.limits,
)
for hit in hits {
let record = self.record(hit.offset, hit.ordinal)
match record.feature.bounds() {
Some(actual) if actual.intersects(bounds) => result.push(record)
_ => ()
}
}
} else {
self.rewind()
while self.next() is Some(record) {
match record.feature.bounds() {
Some(actual) if actual.intersects(bounds) => result.push(record)
_ => ()
}
}
}
result
}
///|
pub fn Reader::validate(self : Reader) -> Unit raise FgbError {
let dataset = self.read_all()
if self.has_index() {
let index = slice_bytes(self.data, self.index_start, self.index_size)
validate_index(
index,
self.header_.features_count,
self.header_.index_node_size,
self.data.length() - self.features_start,
limits=self.limits,
)
let levels = index_levels(
self.header_.features_count,
self.header_.index_node_size,
limits=self.limits,
)
let mut offset = 0
self.rewind()
for i in 0.. ()
_ =>
raise InvalidData(
offset, "indexed feature missing geometry or outside leaf bounds",
)
}
let record = self.record(offset, i)
offset += record.byte_length
}
}
match self.header_.envelope {
None => ()
Some(envelope) =>
for feature in dataset.features {
match feature.bounds() {
Some(bounds) if !envelope.contains(bounds) =>
raise InvalidData(8, "feature outside header envelope")
_ => ()
}
}
}
}