1use std::sync::Arc;
6
7use crate::base::{CellError, CellRef, DateSystem, MAX_COLS, MAX_ROWS, RangeRef, Value};
8use crate::model::{Cell, Sheet, Visibility, Workbook};
9use quick_xml::NsReader;
10use quick_xml::XmlVersion;
11use quick_xml::events::Event;
12use quick_xml::name::ResolveResult;
13
14use crate::xlsx::package::{Package, Rel, normalize, resolve, split_dir};
15use crate::xlsx::{IoError, ReadReport};
16
17const MAIN: &str = "application/vnd.ms-excel.main";
18const BINARY_MAIN: &str = "application/vnd.ms-excel.sheet.binary.macroEnabled.main";
19const WORKSHEET: &str = "application/vnd.ms-excel.worksheet";
20const STRINGS: &str = "application/vnd.ms-excel.sharedStrings";
21const CT_NS: &[u8] = b"http://schemas.openxmlformats.org/package/2006/content-types";
22const REL_NS: &[u8] = b"http://schemas.openxmlformats.org/package/2006/relationships";
23const REL_TYPE: &str = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/";
24const STRICT_REL_TYPE: &str = "http://purl.oclc.org/ooxml/officeDocument/relationships/";
25const MS_REL_TYPE: &str = "http://schemas.microsoft.com/office/2006/relationships/";
26const MAX_META: u64 = 1024 * 1024;
27const MAX_TEXT: usize = 64 * 1024 * 1024;
28const MAX_CELLS: usize = 1_000_000;
29const MAX_STRINGS: usize = 1_000_000;
30const MAX_SHEETS: usize = 1024;
31const MAX_RECORDS: usize = 5_000_000;
32
33fn invalid(msg: impl Into<String>) -> IoError {
34 IoError::Format(format!("XLSB: {}", msg.into()))
35}
36
37#[derive(Default)]
38struct Budget {
39 cells: usize,
40 text: usize,
41}
42impl Budget {
43 fn text(&mut self, len: usize) -> Result<(), IoError> {
44 self.text = self.text.saturating_add(len);
45 if self.text > MAX_TEXT {
46 return Err(IoError::TooLarge("XLSB decoded text exceeds 64 MiB".into()));
47 }
48 Ok(())
49 }
50 fn cell(&mut self) -> Result<(), IoError> {
51 self.cells += 1;
52 if self.cells > MAX_CELLS {
53 return Err(IoError::TooLarge("XLSB exceeds 1 million imported cells".into()));
54 }
55 Ok(())
56 }
57}
58
59pub fn read_xlsb(bytes: &[u8]) -> Result<(Workbook, ReadReport), IoError> {
62 let mut package = Package::open(bytes)?;
63 let types = content_types(&mut package)?;
64 let roots = relationships(&mut package, "")?;
65 let roots: Vec<_> = roots.iter().filter(|r| r.kind == "officeDocument").collect();
66 if roots.len() != 1 {
67 return Err(invalid("expected one workbook relationship"));
68 }
69 let root = roots.first().ok_or_else(|| invalid("missing workbook relationship"))?;
70 if root.external {
71 return Err(invalid("external workbook relationship"));
72 }
73 check_type(&types, &root.target, MAIN)?;
74 let book = required(&mut package, &root.target)?;
75 let mut wb = Workbook::new();
76 wb.sheets.clear();
77 let mut report = ReadReport::default();
78 report.warn("XLSB data-only import: formulas become their saved results and will not recalculate. Formatting, charts, images, macros and other features are not imported; dates, currency and percentages may display as numbers. Save a new .xlsx file; XLSB saving is not supported.");
79 let sheets = workbook(&book, &mut wb, &mut report)?;
80 let rels = relationships(&mut package, &root.target)?;
81 let mut budget = Budget::default();
82 let sst_rels: Vec<_> = rels.iter().filter(|r| r.kind == "sharedStrings").collect();
83 if sst_rels.len() > 1 {
84 return Err(invalid("multiple shared-string relationships"));
85 }
86 let strings = if let Some(rel) = sst_rels.first() {
87 if rel.external {
88 return Err(invalid("external shared-string relationship"));
89 }
90 check_type(&types, &rel.target, STRINGS)?;
91 shared_strings(&required(&mut package, &rel.target)?, &mut budget, &mut report)?
92 } else {
93 vec![]
94 };
95 for info in sheets {
96 let Some(id) = info.rel else {
97 if info.visibility != Visibility::VeryHidden {
98 return Err(invalid("worksheet relationship is null"));
99 }
100 report.warn("XLSB module sheets were skipped.");
101 continue;
102 };
103 let rel = rels.iter().find(|r| r.id == id).ok_or_else(|| invalid("worksheet relationship is missing"))?;
104 if rel.external {
105 return Err(invalid("external sheet relationship"));
106 }
107 if rel.kind != "worksheet" {
108 if !matches!(rel.kind.as_str(), "chartsheet" | "dialogsheet" | "xlMacrosheet" | "xlIntlMacrosheet") {
109 return Err(invalid("unknown sheet relationship type"));
110 }
111 report.warn("XLSB non-worksheet sheets were skipped.");
112 continue;
113 }
114 check_type(&types, &rel.target, WORKSHEET)?;
115 wb.check_sheet_name(&info.name, None).map_err(invalid)?;
116 let mut sheet = worksheet(&required(&mut package, &rel.target)?, &info.name, &strings, &mut budget, &mut report)?;
117 sheet.visibility = info.visibility;
118 wb.sheets.push(Arc::new(sheet));
119 }
120 if wb.sheets.is_empty() {
121 return Err(invalid("no supported worksheets"));
122 }
123 wb.set_active_sheet(wb.sheets.iter().position(|s| s.visibility == Visibility::Visible).unwrap_or(0));
124 Ok((wb, report))
125}
126
127pub(crate) fn is_xlsb(bytes: &[u8]) -> bool {
128 let inspect = || -> Result<bool, IoError> {
129 let mut package = Package::open(bytes)?;
130 let types = content_types(&mut package)?;
131 let roots = relationships(&mut package, "")?;
132 let mut books = roots.iter().filter(|r| r.kind == "officeDocument");
133 let Some(root) = books.next() else { return Ok(false) };
134 Ok(books.next().is_none() && !root.external && check_type(&types, &root.target, MAIN).is_ok())
135 };
136 inspect().unwrap_or(false)
137}
138
139fn required(package: &mut Package<'_>, path: &str) -> Result<Vec<u8>, IoError> {
140 package.read(path)?.ok_or_else(|| invalid(format!("missing part {path}")))
141}
142
143struct Entry {
144 name: String,
145 attrs: Vec<(String, String)>,
146}
147impl Entry {
148 fn attr(&self, name: &str) -> Option<&str> {
149 self.attrs.iter().find(|(k, _)| k == name).map(|(_, v)| v.as_str())
150 }
151}
152
153fn metadata(bytes: &[u8], ns: &[u8], root_name: &[u8]) -> Result<Vec<Entry>, IoError> {
156 let mut reader = NsReader::from_reader(bytes);
157 let mut depth = 0usize;
158 let mut seen = false;
159 let mut entries = vec![];
160 loop {
161 let event = reader.read_event().map_err(|e| IoError::Xml(e.to_string()))?;
162 match &event {
163 Event::Start(e) | Event::Empty(e) => {
164 let (namespace, local) = reader.resolver_mut().resolve_element(e.name());
165 let valid_ns = matches!(namespace, ResolveResult::Bound(n) if n.as_ref() == ns);
166 if depth == 0 {
167 if seen || !valid_ns || local.as_ref() != root_name {
168 return Err(invalid("invalid package metadata root"));
169 }
170 seen = true;
171 } else if depth == 1 && valid_ns {
172 let mut attrs = vec![];
173 for a in e.attributes() {
174 let a = a.map_err(|e| IoError::Xml(e.to_string()))?;
175 if !a.key.as_ref().contains(&b':') {
176 attrs.push((
177 String::from_utf8_lossy(a.key.as_ref()).into_owned(),
178 a.normalized_value(XmlVersion::Explicit1_0).map_err(|e| IoError::Xml(e.to_string()))?.into_owned(),
179 ));
180 }
181 }
182 entries.push(Entry { name: String::from_utf8_lossy(local.as_ref()).into_owned(), attrs });
183 }
184 if matches!(event, Event::Start(_)) {
185 depth += 1;
186 if depth > crate::xlsx::xml::MAX_DEPTH {
187 return Err(invalid("package metadata is nested too deeply"));
188 }
189 }
190 }
191 Event::End(_) => depth = depth.checked_sub(1).ok_or_else(|| invalid("unexpected metadata end"))?,
192 Event::Text(t) if depth == 0 && !t.xml_content(XmlVersion::Explicit1_0).map_err(|e| IoError::Xml(e.to_string()))?.trim().is_empty() => {
193 return Err(invalid("text outside package metadata"));
194 }
195 Event::DocType(_) => return Err(invalid("DOCTYPE in package metadata is unsupported")),
196 Event::Eof => break,
197 _ => {}
198 }
199 }
200 if !seen || depth != 0 {
201 return Err(invalid("incomplete package metadata"));
202 }
203 Ok(entries)
204}
205
206fn content_types(package: &mut Package<'_>) -> Result<Vec<Entry>, IoError> {
207 let data = package.read_limited("[Content_Types].xml", MAX_META)?.ok_or_else(|| invalid("missing content types"))?;
208 metadata(&data, CT_NS, b"Types")
209}
210
211fn check_type(types: &[Entry], path: &str, expected: &str) -> Result<(), IoError> {
212 let found = types.iter().find(|e| e.name == "Override" && e.attr("PartName").is_some_and(|p| normalize(p) == normalize(path))).or_else(|| {
213 types.iter().find(|e| {
214 e.name == "Default" && e.attr("Extension").is_some_and(|ext| path.rsplit('.').next().is_some_and(|p| p.eq_ignore_ascii_case(ext)))
215 })
216 });
217 let actual = found.and_then(|e| e.attr("ContentType"));
218 if actual != Some(expected) && !(expected == MAIN && actual == Some(BINARY_MAIN)) {
219 return Err(invalid(format!("wrong or missing content type for {path}")));
220 }
221 Ok(())
222}
223
224fn relationships(package: &mut Package<'_>, path: &str) -> Result<Vec<Rel>, IoError> {
225 let (dir, file) = split_dir(path);
226 let relpath = if dir.is_empty() { format!("_rels/{file}.rels") } else { format!("{dir}/_rels/{file}.rels") };
227 let data = package.read_limited(&relpath, MAX_META)?.ok_or_else(|| invalid(format!("missing relationships for {path}")))?;
228 let mut out: Vec<Rel> = vec![];
229 for e in metadata(&data, REL_NS, b"Relationships")? {
230 if e.name != "Relationship" {
231 continue;
232 }
233 let id = e.attr("Id").filter(|s| !s.is_empty()).ok_or_else(|| invalid("relationship has no ID"))?;
234 if out.iter().any(|r| r.id == id) {
235 return Err(invalid("duplicate relationship ID"));
236 }
237 let ty = e.attr("Type").ok_or_else(|| invalid("relationship has no type"))?;
238 let kind = ty
239 .strip_prefix(REL_TYPE)
240 .or_else(|| ty.strip_prefix(STRICT_REL_TYPE))
241 .or_else(|| ty.strip_prefix(MS_REL_TYPE))
242 .map(str::to_owned)
243 .unwrap_or_else(|| format!("unsupported:{ty}"));
244 let target = e.attr("Target").ok_or_else(|| invalid("relationship has no target"))?;
245 let external = e.attr("TargetMode") == Some("External");
246 out.push(Rel { id: id.into(), kind, target: if external { target.into() } else { resolve(dir, target) }, external });
247 }
248 Ok(out)
249}
250
251struct Bytes<'a> {
252 rest: &'a [u8],
253}
254impl<'a> Bytes<'a> {
255 fn new(bytes: &'a [u8]) -> Self {
256 Self { rest: bytes }
257 }
258 fn take(&mut self, n: usize) -> Result<&'a [u8], IoError> {
259 let out = self.rest.get(..n).ok_or_else(|| invalid("truncated record or string"))?;
260 self.rest = self.rest.get(n..).ok_or_else(|| invalid("truncated record"))?;
261 Ok(out)
262 }
263 fn u8(&mut self) -> Result<u8, IoError> {
264 self.take(1)?.first().copied().ok_or_else(|| invalid("truncated byte"))
265 }
266 fn u16(&mut self) -> Result<u16, IoError> {
267 Ok(u16::from_le_bytes(self.take(2)?.try_into().map_err(|_| invalid("truncated u16"))?))
268 }
269 fn u32(&mut self) -> Result<u32, IoError> {
270 Ok(u32::from_le_bytes(self.take(4)?.try_into().map_err(|_| invalid("truncated u32"))?))
271 }
272 fn f64(&mut self) -> Result<f64, IoError> {
273 let n = f64::from_le_bytes(self.take(8)?.try_into().map_err(|_| invalid("truncated number"))?);
274 if !n.is_finite() {
275 return Err(invalid("nonfinite numeric value"));
276 }
277 Ok(n)
278 }
279 fn string(&mut self, max: u32, nullable: bool) -> Result<Option<String>, IoError> {
280 let count = self.u32()?;
281 if nullable && count == u32::MAX {
282 return Ok(None);
283 }
284 if count > max {
285 return Err(IoError::TooLarge("XLSB string length exceeds its limit".into()));
286 }
287 let size = (count as usize).checked_mul(2).ok_or_else(|| invalid("string length overflow"))?;
288 let bytes = self.take(size)?;
289 let mut text = String::new();
290 for ch in char::decode_utf16(bytes.as_chunks::<2>().0.iter().copied().map(u16::from_le_bytes)) {
291 text.push(ch.map_err(|_| invalid("invalid UTF-16 string"))?);
292 }
293 Ok(Some(text))
294 }
295 fn text(&mut self, max: u32) -> Result<String, IoError> {
296 self.string(max, false)?.ok_or_else(|| invalid("null string"))
297 }
298 fn finish(self) -> Result<(), IoError> {
299 if !self.rest.is_empty() {
300 return Err(invalid("unexpected bytes after record payload"));
301 }
302 Ok(())
303 }
304}
305
306struct Records<'a> {
307 bytes: Bytes<'a>,
308 count: usize,
309 blocks: Vec<u16>,
310}
311impl<'a> Records<'a> {
312 fn new(bytes: &'a [u8]) -> Self {
313 Self { bytes: Bytes::new(bytes), count: 0, blocks: vec![] }
314 }
315 fn next(&mut self, report: &mut ReadReport) -> Result<Option<(u16, &'a [u8])>, IoError> {
316 while !self.bytes.rest.is_empty() {
317 self.count += 1;
318 if self.count > MAX_RECORDS {
319 return Err(IoError::TooLarge("XLSB part contains too many records".into()));
320 }
321 let first = self.bytes.u8()?;
322 let id = if first & 0x80 == 0 {
323 first as u16
324 } else {
325 let id = u16::from(first & 0x7f) | (u16::from(self.bytes.u8()? & 0x7f) << 7);
326 if id < 128 {
327 return Err(invalid("invalid two-byte record type"));
328 }
329 id
330 };
331 let mut size = 0usize;
332 for shift in [0, 7, 14, 21] {
333 let b = self.bytes.u8()?;
334 size |= usize::from(b & 0x7f) << shift;
335 if b & 0x80 == 0 || shift == 21 {
337 break;
338 }
339 }
340 let payload = self.bytes.take(size)?;
341 match id {
342 35 | 37 => {
343 let mut b = Bytes::new(payload);
344 if id == 35 {
345 b.take(4)?;
346 } else {
347 let count = b.u16()?;
348 if count == 0 {
349 return Err(invalid("empty alternate-content version list"));
350 }
351 b.take(usize::from(count) * 4)?;
352 }
353 b.finish()?;
354 if self.blocks.len() >= 64 {
355 return Err(invalid("extension blocks nested too deeply"));
356 }
357 self.blocks.push(id + 1);
358 report.warn("Unsupported XLSB future-record and alternate-content blocks were skipped.");
359 }
360 36 | 38 => {
361 if self.blocks.pop() != Some(id) || !payload.is_empty() {
362 return Err(invalid("mismatched extension block end"));
363 }
364 }
365 _ if !self.blocks.is_empty() => {}
366 _ => return Ok(Some((id, payload))),
367 }
368 }
369 if !self.blocks.is_empty() {
370 return Err(invalid("unterminated extension block"));
371 }
372 Ok(None)
373 }
374 fn first(&mut self, id: u16, report: &mut ReadReport) -> Result<(), IoError> {
375 if self.next(report)? != Some((id, &[])) {
376 return Err(invalid("missing part begin record"));
377 }
378 Ok(())
379 }
380 fn end(&mut self, payload: &[u8], report: &mut ReadReport) -> Result<(), IoError> {
381 if !payload.is_empty() || self.next(report)?.is_some() {
382 return Err(invalid("records after part end"));
383 }
384 Ok(())
385 }
386}
387
388struct SheetInfo {
389 name: String,
390 rel: Option<String>,
391 visibility: Visibility,
392}
393fn workbook(bytes: &[u8], wb: &mut Workbook, report: &mut ReadReport) -> Result<Vec<SheetInfo>, IoError> {
394 let mut records = Records::new(bytes);
395 records.first(131, report)?;
396 let mut sheets = vec![];
397 let mut bundle = false;
398 let mut bundle_seen = false;
399 while let Some((id, payload)) = records.next(report)? {
400 let mut b = Bytes::new(payload);
401 match id {
402 132 => {
403 if bundle || !bundle_seen {
404 return Err(invalid("incomplete worksheet list"));
405 }
406 records.end(payload, report)?;
407 return Ok(sheets);
408 }
409 143 => {
410 if bundle_seen {
411 return Err(invalid("duplicate worksheet list"));
412 }
413 bundle_seen = true;
414 bundle = true;
415 b.finish()?;
416 }
417 144 => {
418 if !bundle {
419 return Err(invalid("unexpected worksheet list end"));
420 }
421 bundle = false;
422 b.finish()?;
423 }
424 153 => {
425 wb.date_system = if b.u32()? & 1 != 0 { DateSystem::D1904 } else { DateSystem::D1900 };
426 b.u32()?;
427 b.text(31)?;
428 b.finish()?;
429 }
430 156 => {
431 if !bundle {
432 return Err(invalid("worksheet outside the worksheet list"));
433 }
434 if sheets.len() >= MAX_SHEETS {
435 return Err(IoError::TooLarge("XLSB exceeds 1024 sheets".into()));
436 }
437 let visibility = match b.u32()? {
438 0 => Visibility::Visible,
439 1 => Visibility::Hidden,
440 2 => Visibility::VeryHidden,
441 _ => return Err(invalid("invalid sheet visibility")),
442 };
443 let sheet_id = b.u32()?;
444 if !(1..=65535).contains(&sheet_id) {
445 return Err(invalid("invalid worksheet ID"));
446 }
447 let rel = b.string(255, true)?;
448 let name = b.text(31)?;
449 b.finish()?;
450 sheets.push(SheetInfo { name, rel, visibility });
451 }
452 _ => {}
453 }
454 }
455 Err(invalid("missing workbook end record"))
456}
457
458fn rich_string(b: &mut Bytes<'_>) -> Result<String, IoError> {
459 let flags = b.u8()?;
460 let text = b.text(32767)?;
461 if flags & 1 != 0 {
462 let count = b.u32()?;
463 if count > 32767 {
464 return Err(invalid("too many rich-text runs"));
465 }
466 b.take(count as usize * 4)?;
467 }
468 if flags & 2 != 0 {
469 b.text(32767)?;
470 let count = b.u32()?;
471 if count > 32767 {
472 return Err(invalid("too many phonetic runs"));
473 }
474 b.take(count as usize * 10)?;
480 b.rest = &[];
481 }
482 Ok(text)
483}
484
485fn shared_strings(bytes: &[u8], budget: &mut Budget, report: &mut ReadReport) -> Result<Vec<Arc<str>>, IoError> {
486 let mut records = Records::new(bytes);
487 let Some((159, payload)) = records.next(report)? else {
488 return Err(invalid("missing shared-string begin record"));
489 };
490 let mut header = Bytes::new(payload);
491 let total = header.u32()?;
492 let unique = header.u32()?;
493 header.finish()?;
494 if unique > total || unique as usize > MAX_STRINGS {
495 return Err(IoError::TooLarge("invalid or oversized shared-string count".into()));
496 }
497 let mut strings = vec![];
498 while let Some((id, payload)) = records.next(report)? {
499 match id {
500 160 => {
501 records.end(payload, report)?;
502 if strings.len() != unique as usize {
503 return Err(invalid("shared-string count does not match records"));
504 }
505 return Ok(strings);
506 }
507 19 => {
508 if strings.len() >= unique as usize {
509 return Err(invalid("too many shared-string items"));
510 }
511 let mut b = Bytes::new(payload);
512 let text = rich_string(&mut b)?;
513 b.finish()?;
514 budget.text(text.len())?;
515 strings.push(Arc::from(text));
516 }
517 _ => return Err(invalid("unexpected shared-string record")),
518 }
519 }
520 Err(invalid("missing shared-string end record"))
521}
522
523fn error(code: u8) -> Result<CellError, IoError> {
524 Ok(match code {
525 0x00 => CellError::Null,
526 0x07 => CellError::Div0,
527 0x0f => CellError::Value,
528 0x17 => CellError::Ref,
529 0x1d => CellError::Name,
530 0x24 => CellError::Num,
531 0x2a => CellError::NA,
532 0x2b => CellError::GettingData,
533 _ => return Err(invalid("unknown cell error code")),
534 })
535}
536fn boolean(b: &mut Bytes<'_>) -> Result<bool, IoError> {
537 match b.u8()? {
538 0 => Ok(false),
539 1 => Ok(true),
540 _ => Err(invalid("invalid boolean value")),
541 }
542}
543fn cached_formula(b: &mut Bytes<'_>) -> Result<(), IoError> {
544 b.take(2)?;
545 let cce = b.u32()?;
546 if !(1..=16384).contains(&cce) {
547 return Err(invalid("invalid formula token length"));
548 }
549 b.take(cce as usize)?;
550 let extra = b.u32()?;
551 b.take(extra as usize)?;
552 Ok(())
553}
554fn value(id: u16, b: &mut Bytes<'_>, strings: &[Arc<str>], budget: &mut Budget) -> Result<Value, IoError> {
555 let value = match id {
556 1 => Value::Empty,
557 2 => {
558 let rk = b.u32()?;
559 let n = if rk & 2 != 0 { ((rk as i32) >> 2) as f64 } else { f64::from_bits(u64::from(rk & !3) << 32) };
560 let n = if rk & 1 != 0 { n / 100.0 } else { n };
561 if !n.is_finite() {
562 return Err(invalid("nonfinite RK number"));
563 }
564 Value::Number(n)
565 }
566 3 | 11 => Value::Error(error(b.u8()?)?),
567 4 | 10 => Value::Bool(boolean(b)?),
568 5 | 9 => Value::Number(b.f64()?),
569 6 | 8 | 62 => {
570 let s = if id == 62 { rich_string(b)? } else { b.text(32767)? };
571 budget.text(s.len())?;
572 Value::Text(Arc::from(s))
573 }
574 7 => Value::Text(strings.get(b.u32()? as usize).cloned().ok_or_else(|| invalid("shared-string index is out of bounds"))?),
575 _ => return Err(invalid("unsupported cell record")),
576 };
577 if (8..=11).contains(&id) {
578 cached_formula(b)?;
579 }
580 Ok(value)
581}
582
583fn worksheet(bytes: &[u8], name: &str, strings: &[Arc<str>], budget: &mut Budget, report: &mut ReadReport) -> Result<Sheet, IoError> {
584 let mut records = Records::new(bytes);
585 records.first(129, report)?;
586 let mut sheet = Sheet::new(name);
587 let mut data = false;
588 let mut data_seen = false;
589 let mut row = None;
590 let mut col = None;
591 while let Some((id, payload)) = records.next(report)? {
592 let mut b = Bytes::new(payload);
593 match id {
594 130 => {
595 if data || !data_seen {
596 return Err(invalid("incomplete worksheet data"));
597 }
598 records.end(payload, report)?;
599 return Ok(sheet);
600 }
601 145 => {
602 if data_seen {
603 return Err(invalid("duplicate worksheet data"));
604 }
605 data_seen = true;
606 data = true;
607 b.finish()?;
608 }
609 146 => {
610 if !data {
611 return Err(invalid("unexpected worksheet data end"));
612 }
613 data = false;
614 b.finish()?;
615 }
616 0 => {
617 if !data {
618 return Err(invalid("row outside worksheet data"));
619 }
620 let r = b.u32()?;
621 if r >= MAX_ROWS || row.is_some_and(|previous| r <= previous) {
622 return Err(invalid("row is out of bounds or unordered"));
623 }
624 b.take(9)?;
625 let spans = b.u32()?;
626 if spans > 16 {
627 return Err(invalid("too many row spans"));
628 }
629 b.take(spans as usize * 8)?;
630 b.finish()?;
631 row = Some(r);
632 col = None;
633 }
634 1..=11 | 62 => {
635 if !data {
636 return Err(invalid("cell outside worksheet data"));
637 }
638 let r = row.ok_or_else(|| invalid("cell before row header"))?;
639 let c = b.u32()?;
640 if c >= MAX_COLS || col.is_some_and(|previous| c <= previous) {
641 return Err(invalid("column is out of bounds or unordered"));
642 }
643 b.u32()?; let value = value(id, &mut b, strings, budget)?;
645 b.finish()?;
646 if !value.is_empty() {
647 budget.cell()?;
648 sheet.cells.set(CellRef::new(r, c), Cell::value(value));
649 }
650 col = Some(c);
651 }
652 176 => {
653 if data {
654 return Err(invalid("merge inside worksheet data"));
655 }
656 let r0 = b.u32()?;
657 let r1 = b.u32()?;
658 let c0 = b.u32()?;
659 let c1 = b.u32()?;
660 b.finish()?;
661 if r0 > r1 || c0 > c1 || r1 >= MAX_ROWS || c1 >= MAX_COLS {
662 report.warn("Invalid XLSB merged ranges were skipped.");
663 continue;
664 }
665 if sheet.merges.len() >= 100_000 {
666 return Err(IoError::TooLarge("XLSB exceeds 100000 merges".into()));
667 }
668 sheet.merges.push(RangeRef::new(CellRef::new(r0, c0), CellRef::new(r1, c1)));
669 }
670 _ => {}
671 }
672 }
673 Err(invalid("missing worksheet end record"))
674}