Skip to content

Commit a6a7437

Browse files
p4kenCopilot
andcommitted
Reduce RAM usage
Co-authored-by: Copilot <copilot@github.com>
1 parent ec8e46c commit a6a7437

2 files changed

Lines changed: 67 additions & 40 deletions

File tree

src/v0_6_1/ser/prop/flat.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ impl FlatProperties {
6060
pub fn get(&self, key: &str) -> Option<&FieldValue<'static>> {
6161
self.entries.iter().find(|(k, _)| k == key).map(|(_, v)| v)
6262
}
63+
64+
/// Consume the flattened properties, returning the underlying entries.
65+
pub fn into_entries(self) -> Vec<(Cow<'static, str>, FieldValue<'static>)> {
66+
self.entries
67+
}
6368
}
6469

6570
impl SerializeProperties for &mut FlatProperties {

src/v0_6_2/fgb/ser.rs

Lines changed: 62 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,26 @@ use crate::v0_6_1::ser::{
1616
/// Collects features in two phases:
1717
/// 1. `add_feature()` flattens each feature's properties and stores them with geometry.
1818
/// 2. After sorting `keys()`, call `write_features()` with an `FgbWriter` to emit all features.
19+
///
20+
/// プロパティのキーは layer 全体で一度だけ heap に持ち、各 feature は
21+
/// `(column_idx, value)` の疎なリストとして保持する。これにより同一キーの
22+
/// 文字列が feature ごとに複製されず、メモリ消費を抑えられる。
1923
pub struct LayerSerializer {
20-
/// Union of all keys seen across all features, in insertion order.
21-
all_keys: Vec<String>,
22-
key_set: std::collections::BTreeSet<String>,
23-
/// Per-feature geometry.
24+
column_idx: std::collections::HashMap<String, usize>,
25+
columns: Vec<String>,
26+
/// 各列の型。各列で最初に登場した値の型を採用する。
27+
column_types: Vec<flatgeobuf::ColumnType>,
2428
geometries: Vec<geo_types::Geometry<f64>>,
25-
/// Per-feature flattened properties.
26-
features: Vec<FlatProperties>,
29+
/// Per-feature sparse properties: `(column_idx, value)` のリスト。
30+
features: Vec<Vec<(usize, FieldValue<'static>)>>,
2731
}
2832

2933
impl LayerSerializer {
3034
pub fn new() -> Self {
3135
LayerSerializer {
32-
all_keys: Vec::new(),
33-
key_set: std::collections::BTreeSet::new(),
36+
column_idx: std::collections::HashMap::new(),
37+
columns: Vec::new(),
38+
column_types: Vec::new(),
3439
geometries: Vec::new(),
3540
features: Vec::new(),
3641
}
@@ -48,55 +53,72 @@ impl LayerSerializer {
4853
geometry: impl Into<geo_types::Geometry<f64>>,
4954
properties: impl serde::Serialize,
5055
) {
51-
let flat = FlatProperties::flatten(properties).unwrap();
52-
for key in flat.keys() {
53-
if self.key_set.insert(key.to_owned()) {
54-
self.all_keys.push(key.to_owned());
55-
}
56-
}
56+
let entries = FlatProperties::flatten(properties).unwrap().into_entries();
57+
let feat_vals = entries
58+
.into_iter()
59+
.map(|(key, value)| {
60+
let idx = self
61+
.column_idx
62+
.get(key.as_ref())
63+
.copied()
64+
.unwrap_or_else(|| {
65+
let i = self.columns.len();
66+
let owned = key.into_owned();
67+
self.columns.push(owned.clone());
68+
self.column_types
69+
.push(crate::v0_6_1::fgb::ser::prop::to_column_type(&value));
70+
self.column_idx.insert(owned, i);
71+
i
72+
});
73+
(idx, value)
74+
})
75+
.collect();
5776
self.geometries.push(geometry.into());
58-
self.features.push(flat);
77+
self.features.push(feat_vals);
5978
}
6079

6180
/// Returns an iterator over the union of all flattened keys.
6281
pub fn keys(&self) -> impl Iterator<Item = &str> {
63-
self.all_keys.iter().map(|s| s.as_str())
82+
self.columns.iter().map(|s| s.as_str())
6483
}
6584

6685
/// Set the column order to use when writing features.
86+
///
87+
/// `columns` には既存の全列を含めること(順序入れ替えのみ可、列の追加・削除は不可)。
6788
pub fn set_columns(&mut self, columns: Vec<String>) {
68-
self.all_keys = columns;
89+
let new_idx: std::collections::HashMap<String, usize> =
90+
columns.iter().cloned().zip(0..).collect();
91+
let remap: Vec<usize> = self.columns.iter().map(|k| new_idx[k]).collect();
92+
let mut new_types = vec![flatgeobuf::ColumnType::String; columns.len()];
93+
for (old, &ty) in self.column_types.iter().enumerate() {
94+
new_types[remap[old]] = ty;
95+
}
96+
for feat in &mut self.features {
97+
for (idx, _) in feat {
98+
*idx = remap[*idx];
99+
}
100+
}
101+
self.columns = columns;
102+
self.column_idx = new_idx;
103+
self.column_types = new_types;
69104
}
70105

71106
/// Write all collected features to the FgbWriter using the current key order.
72107
///
73-
/// `FgbWriter::property` は連番idxでしか列を auto-declare しない仕様のため、
74-
/// 全 feature を書く前に `add_column` で列を宣言してしまう。型は各 key の
75-
/// 全 feature 中で最初に現れた値の型を採用し、以降の衝突は無視する。
76-
/// 全 feature で値が無い列は String として宣言(実際には誰も書かないので無害)。
108+
/// `FgbWriter::property` は連番 idx でしか列を auto-declare しない仕様のため、
109+
/// 全 feature を書く前に `add_column` で列を宣言してしまう。
77110
pub fn write_features(self, writer: &mut FgbWriter<'_>) -> Result<(), Error> {
78-
for key in &self.all_keys {
79-
let col_type = self
80-
.features
81-
.iter()
82-
.find_map(|f| f.get(key).map(crate::v0_6_1::fgb::ser::prop::to_column_type))
83-
.unwrap_or(flatgeobuf::ColumnType::String);
84-
writer.add_column(key, col_type, |_, _| {});
111+
for (key, &ty) in self.columns.iter().zip(self.column_types.iter()) {
112+
writer.add_column(key, ty, |_, _| {});
85113
}
86-
87-
for i in 0..self.features.len() {
88-
process_geometry(&self.geometries[i], writer)?;
89-
let feat = &self.features[i];
90-
for (idx, key) in self.all_keys.iter().enumerate() {
91-
let column_value = match feat.get(key) {
92-
Some(value) => crate::v0_6_1::fgb::ser::prop::to_column_value(value),
93-
None => continue,
94-
};
114+
for (feat, geom) in self.features.iter().zip(self.geometries.iter()) {
115+
process_geometry(geom, writer)?;
116+
for (idx, val) in feat {
95117
flatgeobuf::geozero::PropertyProcessor::property(
96118
writer,
97-
idx,
98-
key.as_ref(),
99-
&column_value,
119+
*idx,
120+
self.columns[*idx].as_ref(),
121+
&crate::v0_6_1::fgb::ser::prop::to_column_value(val),
100122
)?;
101123
}
102124
flatgeobuf::geozero::FeatureProcessor::feature_end(writer, 0)?;

0 commit comments

Comments
 (0)