diff --git a/SpreadCheetah.Test/Helpers/CellFactory.cs b/SpreadCheetah.Test/Helpers/CellFactory.cs index 6a12f669..acf6832b 100644 --- a/SpreadCheetah.Test/Helpers/CellFactory.cs +++ b/SpreadCheetah.Test/Helpers/CellFactory.cs @@ -93,7 +93,8 @@ internal static class CellFactory public static object Create(CellType cellType, CellValueType valueType, bool isNull, StyleId? styleId, out object? value) => valueType switch { - CellValueType.Bool => CreateForBool(cellType, isNull, styleId, out value), + CellValueType.BoolFalse => CreateForBool(cellType, isNull ? null : false, styleId, out value), + CellValueType.BoolTrue => CreateForBool(cellType, isNull ? null : true, styleId, out value), CellValueType.DateTime => CreateForDateTime(cellType, isNull, styleId, out value), CellValueType.Decimal => CreateForDecimal(cellType, isNull, styleId, out value), CellValueType.Double => CreateForDouble(cellType, isNull, styleId, out value), @@ -106,7 +107,8 @@ internal static class CellFactory public static Cell Create(Formula formula, CellValueType valueType, bool isNull, StyleId? styleId, out object? value) => valueType switch { - CellValueType.Bool => CreateForBool(formula, isNull, styleId, out value), + CellValueType.BoolFalse => CreateForBool(formula, isNull ? null : false, styleId, out value), + CellValueType.BoolTrue => CreateForBool(formula, isNull ? null : true, styleId, out value), CellValueType.DateTime => CreateForDateTime(formula, isNull, styleId, out value), CellValueType.Decimal => CreateForDecimal(formula, isNull, styleId, out value), CellValueType.Double => CreateForDouble(formula, isNull, styleId, out value), @@ -117,9 +119,8 @@ internal static class CellFactory _ => throw new ArgumentOutOfRangeException(nameof(valueType), valueType, null) }; - private static object CreateForBool(CellType cellType, bool isNull, StyleId? styleId, out object? value) + private static object CreateForBool(CellType cellType, bool? actualValue, StyleId? styleId, out object? value) { - bool? actualValue = isNull ? null : true; value = actualValue; return cellType switch @@ -131,9 +132,8 @@ private static object CreateForBool(CellType cellType, bool isNull, StyleId? sty }; } - private static Cell CreateForBool(Formula formula, bool isNull, StyleId? styleId, out object? value) + private static Cell CreateForBool(Formula formula, bool? actualValue, StyleId? styleId, out object? value) { - bool? actualValue = !isNull ? true : null; value = actualValue; return new Cell(formula, actualValue, styleId); } diff --git a/SpreadCheetah.Test/Helpers/CellValueType.cs b/SpreadCheetah.Test/Helpers/CellValueType.cs index 40c95158..11760c3d 100644 --- a/SpreadCheetah.Test/Helpers/CellValueType.cs +++ b/SpreadCheetah.Test/Helpers/CellValueType.cs @@ -9,5 +9,6 @@ public enum CellValueType Double, Decimal, DateTime, - Bool + BoolTrue, + BoolFalse } diff --git a/SpreadCheetah.Test/Helpers/TestData.cs b/SpreadCheetah.Test/Helpers/TestData.cs index 8e946996..9b8ec79d 100644 --- a/SpreadCheetah.Test/Helpers/TestData.cs +++ b/SpreadCheetah.Test/Helpers/TestData.cs @@ -5,7 +5,7 @@ namespace SpreadCheetah.Test.Helpers; internal static class TestData { private static readonly CellType[] CellTypeArray = EnumHelper.GetValues(); - private static readonly CellType[] StyledCellTypeArray = new[] { CellType.StyledCell, CellType.Cell }; + private static readonly CellType[] StyledCellTypeArray = [CellType.StyledCell, CellType.Cell]; private static readonly CellValueType[] CellValueTypeArray = EnumHelper.GetValues(); private static readonly RowCollectionType[] RowCollectionTypeArray = EnumHelper.GetValues(); diff --git a/SpreadCheetah.Test/Tests/SpreadsheetFormulaRowTests.cs b/SpreadCheetah.Test/Tests/SpreadsheetFormulaRowTests.cs index 733bc75d..c123ffb7 100644 --- a/SpreadCheetah.Test/Tests/SpreadsheetFormulaRowTests.cs +++ b/SpreadCheetah.Test/Tests/SpreadsheetFormulaRowTests.cs @@ -53,21 +53,19 @@ public async Task Spreadsheet_AddRow_CellWithFormulaAndStyle(bool bold) // Arrange const string formulaText = "SUM(A1,A2)"; using var stream = new MemoryStream(); - await using (var spreadsheet = await Spreadsheet.CreateNewAsync(stream)) - { - await spreadsheet.StartWorksheetAsync("Sheet"); + await using var spreadsheet = await Spreadsheet.CreateNewAsync(stream); + await spreadsheet.StartWorksheetAsync("Sheet"); - var style = new Style(); - style.Font.Bold = bold; - var styleId = spreadsheet.AddStyle(style); + var style = new Style(); + style.Font.Bold = bold; + var styleId = spreadsheet.AddStyle(style); - var formula = new Formula(formulaText); - var cell = new Cell(formula, styleId); + var formula = new Formula(formulaText); + var cell = new Cell(formula, styleId); - // Act - await spreadsheet.AddRowAsync(cell); - await spreadsheet.FinishAsync(); - } + // Act + await spreadsheet.AddRowAsync(cell); + await spreadsheet.FinishAsync(); // Assert SpreadsheetAssert.Valid(stream); @@ -108,30 +106,54 @@ public async Task Spreadsheet_AddRow_CellWithFormulaAndCachedValue(CellValueType Assert.Equal(valueType.GetExpectedDefaultNumberFormat() ?? "", actualCell.Style.NumberFormat.Format); } + [Fact] + public async Task Spreadsheet_AddRow_CellWithFormulaAndCachedDateTimeValueWithoutDefaultStyle() + { + // Arrange + const string formulaText = "SUM(A1,A2)"; + using var stream = new MemoryStream(); + var options = new SpreadCheetahOptions { DefaultDateTimeFormat = null }; + await using var spreadsheet = await Spreadsheet.CreateNewAsync(stream, options); + await spreadsheet.StartWorksheetAsync("Sheet"); + var formula = new Formula(formulaText); + var cachedValue = new DateTime(2020, 1, 2, 3, 4, 5, DateTimeKind.Utc); + var cell = new Cell(formula, cachedValue); + + // Act + await spreadsheet.AddRowAsync(cell); + await spreadsheet.FinishAsync(); + + // Assert + SpreadsheetAssert.Valid(stream); + using var workbook = new XLWorkbook(stream); + var worksheet = workbook.Worksheets.Single(); + var actualCell = worksheet.Cell(1, 1); + Assert.Equal(formulaText, actualCell.FormulaA1); + Assert.Equal(cachedValue.ToOADate(), actualCell.GetValue(), 0.0001); + } + + public static IEnumerable ItalicWithValueTypes() => TestData.CombineWithValueTypes(true, false); + [Theory] - [InlineData(true)] - [InlineData(false)] - public async Task Spreadsheet_AddRow_CellWithFormulaAndStyleAndCachedValue(bool italic) + [MemberData(nameof(ItalicWithValueTypes))] + public async Task Spreadsheet_AddRow_CellWithFormulaAndStyleAndCachedValue(bool italic, CellValueType cachedValueType, RowCollectionType rowType, bool cachedValueIsNull) { // Arrange const string formulaText = "SUM(A1,A2)"; - const int cachedValue = int.MinValue; using var stream = new MemoryStream(); - await using (var spreadsheet = await Spreadsheet.CreateNewAsync(stream)) - { - await spreadsheet.StartWorksheetAsync("Sheet"); + await using var spreadsheet = await Spreadsheet.CreateNewAsync(stream); + await spreadsheet.StartWorksheetAsync("Sheet"); - var style = new Style(); - style.Font.Italic = italic; - var styleId = spreadsheet.AddStyle(style); + var style = new Style(); + style.Font.Italic = italic; + var styleId = spreadsheet.AddStyle(style); - var formula = new Formula(formulaText); - var cell = new Cell(formula, cachedValue, styleId); + var formula = new Formula(formulaText); + var cell = CellFactory.Create(formula, cachedValueType, cachedValueIsNull, styleId, out var cachedValue); - // Act - await spreadsheet.AddRowAsync(cell); - await spreadsheet.FinishAsync(); - } + // Act + await spreadsheet.AddRowAsync(cell, rowType); + await spreadsheet.FinishAsync(); // Assert SpreadsheetAssert.Valid(stream); @@ -140,7 +162,7 @@ public async Task Spreadsheet_AddRow_CellWithFormulaAndStyleAndCachedValue(bool var actualCell = worksheet.Cell(1, 1); Assert.Equal(formulaText, actualCell.FormulaA1); Assert.Equal(italic, actualCell.Style.Font.Italic); - Assert.Equal(cachedValue, actualCell.CachedValue.GetNumber()); + Assert.Equal(cachedValue.GetExpectedCachedValueAsString(), actualCell.CachedValue.ToString(CultureInfo.InvariantCulture)); } [Theory] diff --git a/SpreadCheetah.Test/Tests/SpreadsheetRowTests.cs b/SpreadCheetah.Test/Tests/SpreadsheetRowTests.cs index 5d1b4758..212030d0 100644 --- a/SpreadCheetah.Test/Tests/SpreadsheetRowTests.cs +++ b/SpreadCheetah.Test/Tests/SpreadsheetRowTests.cs @@ -652,6 +652,33 @@ public async Task Spreadsheet_AddRow_ExplicitCellReferencesForLongStringValueCel Assert.Equal(expectedRow1Refs, actualSheet2Refs); } + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task Spreadsheet_AddRow_ExplicitCellReferenceForDateTimeCellWithDefaultStyling(bool isNull) + { + // Arrange + using var stream = new MemoryStream(); + var options = new SpreadCheetahOptions { WriteCellReferenceAttributes = true }; + await using var spreadsheet = await Spreadsheet.CreateNewAsync(stream, options); + await spreadsheet.StartWorksheetAsync("Sheet1"); + + DateTime? cellValue = isNull ? null : new DateTime(2019, 8, 7, 6, 5, 4, DateTimeKind.Utc); + var cell = new Cell(cellValue); + + // Act + await spreadsheet.AddRowAsync(cell); + await spreadsheet.FinishAsync(); + + // Assert + SpreadsheetAssert.Valid(stream); + using var actual = SpreadsheetDocument.Open(stream, true); + var sheetPart = Assert.Single(actual.WorkbookPart!.WorksheetParts); + var actualRow = Assert.Single(sheetPart.Worksheet.Descendants()); + var actualCell = Assert.Single(actualRow.Descendants()); + Assert.Equal("A1", actualCell.CellReference?.Value); + } + public static IEnumerable RowHeights() => TestData.CombineWithCellTypes( 0.1, 10d, diff --git a/SpreadCheetah.Test/Tests/SpreadsheetStyledRowTests.cs b/SpreadCheetah.Test/Tests/SpreadsheetStyledRowTests.cs index 233b1b56..82ca5da6 100644 --- a/SpreadCheetah.Test/Tests/SpreadsheetStyledRowTests.cs +++ b/SpreadCheetah.Test/Tests/SpreadsheetStyledRowTests.cs @@ -1043,6 +1043,50 @@ public async Task Spreadsheet_AddRow_ExplicitCellReferencesForStyledCells(CellVa Assert.Equal(expectedRow1Refs, actualSheet2Refs); } + [Theory] + [MemberData(nameof(TestData.StyledCellTypes), MemberType = typeof(TestData))] + public async Task Spreadsheet_AddRow_LongStringValueStyledCells(CellType cellType, RowCollectionType rowType) + { + // Arrange + using var stream = new MemoryStream(); + var options = new SpreadCheetahOptions { BufferSize = SpreadCheetahOptions.MinimumBufferSize }; + await using var spreadsheet = await Spreadsheet.CreateNewAsync(stream, options); + var value = new string('a', options.BufferSize * 2); + var style = new Style(); + style.Fill.Color = Color.Thistle; + var styleId = spreadsheet.AddStyle(style); + + var row1 = Enumerable.Range(1, 10).Select(_ => CellFactory.Create(cellType, value, styleId)).ToList(); + var row2 = Enumerable.Range(1, 1).Select(_ => CellFactory.Create(cellType, value, styleId)).ToList(); + var row3 = Enumerable.Range(1, 100).Select(_ => CellFactory.Create(cellType, value, styleId)).ToList(); + + // Act + await spreadsheet.StartWorksheetAsync("Sheet1"); + await spreadsheet.AddRowAsync(row1, rowType); + await spreadsheet.AddRowAsync(row2, rowType); + await spreadsheet.AddRowAsync(row3, rowType); + + await spreadsheet.StartWorksheetAsync("Sheet2"); + await spreadsheet.AddRowAsync(row1, rowType); + + await spreadsheet.FinishAsync(); + + // Assert + SpreadsheetAssert.Valid(stream); + using var actual = SpreadsheetDocument.Open(stream, true); + var sheetParts = actual.WorkbookPart!.WorksheetParts.ToList(); + Assert.Equal(2, sheetParts.Count); + + var sheet1Rows = sheetParts[0].Worksheet.Descendants().ToList(); + Assert.Equal(3, sheet1Rows.Count); + Assert.All(sheet1Rows[0].Descendants(), x => Assert.Equal(value, x.InnerText)); + Assert.All(sheet1Rows[1].Descendants(), x => Assert.Equal(value, x.InnerText)); + Assert.All(sheet1Rows[2].Descendants(), x => Assert.Equal(value, x.InnerText)); + + var sheet2Row = Assert.Single(sheetParts[1].Worksheet.Descendants()); + Assert.All(sheet2Row.Descendants(), x => Assert.Equal(value, x.InnerText)); + } + [Theory] [MemberData(nameof(TestData.StyledCellTypes), MemberType = typeof(TestData))] public async Task Spreadsheet_AddRow_ExplicitCellReferencesForLongStringValueStyledCells(CellType cellType, RowCollectionType rowType) diff --git a/SpreadCheetah/CellValueWriters/Boolean/BooleanCellValueWriter.cs b/SpreadCheetah/CellValueWriters/Boolean/BooleanCellValueWriter.cs index b0f33783..3ee759b0 100644 --- a/SpreadCheetah/CellValueWriters/Boolean/BooleanCellValueWriter.cs +++ b/SpreadCheetah/CellValueWriters/Boolean/BooleanCellValueWriter.cs @@ -7,13 +7,15 @@ namespace SpreadCheetah.CellValueWriters.Boolean; internal abstract class BooleanCellValueWriter : CellValueWriter { - private static ReadOnlySpan BeginStyledBooleanCell => " BeginBooleanFormulaCell => ""u8; + protected static ReadOnlySpan BeginStyledBooleanCell => " BeginBooleanFormulaCell => ""u8; + protected static ReadOnlySpan EndReferenceBeginStyled => "\" t=\"b\" s=\""u8; + protected static ReadOnlySpan EndReferenceBeginFormula => "\" t=\"b\">"u8; protected abstract bool TryWriteCell(SpreadsheetBuffer buffer); + protected abstract bool TryWriteCell(StyleId styleId, SpreadsheetBuffer buffer); protected abstract bool TryWriteCellWithReference(CellWriterState state); - protected abstract bool TryWriteEndStyleValue(Span bytes, out int bytesWritten); - protected abstract bool TryWriteEndFormulaValue(Span bytes, out int bytesWritten); + protected abstract bool TryWriteCellWithReference(StyleId styleId, CellWriterState state); public override bool TryWriteCell(in DataCell cell, DefaultStyling? defaultStyling, SpreadsheetBuffer buffer) { @@ -25,43 +27,6 @@ public override bool TryWriteCell(in DataCell cell, StyleId styleId, Spreadsheet return TryWriteCell(styleId, buffer); } - private bool TryWriteCell(StyleId styleId, SpreadsheetBuffer buffer) - { - var bytes = buffer.GetSpan(); - var written = 0; - - if (!BeginStyledBooleanCell.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(styleId.Id, bytes, ref written)) return false; - if (!TryWriteEndStyleValue(bytes.Slice(written), out var endLength)) return false; - written += endLength; - - buffer.Advance(written); - return true; - } - - public override bool TryWriteCell(string formulaText, in DataCell cachedValue, StyleId? styleId, DefaultStyling? defaultStyling, SpreadsheetBuffer buffer) - { - var bytes = buffer.GetSpan(); - var written = 0; - - if (styleId is null) - { - if (!BeginBooleanFormulaCell.TryCopyTo(bytes, ref written)) return false; - } - else - { - if (!BeginStyledBooleanCell.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(styleId.Id, bytes, ref written)) return false; - if (!FormulaCellHelper.EndStyleBeginFormula.TryCopyTo(bytes, ref written)) return false; - } - - if (!SpanHelper.TryWrite(formulaText, bytes, ref written)) return false; - if (!TryWriteEndFormulaValue(bytes.Slice(written), out var endLength)) return false; - - buffer.Advance(written + endLength); - return true; - } - public override bool TryWriteCellWithReference(in DataCell cell, DefaultStyling? defaultStyling, CellWriterState state) { return TryWriteCellWithReference(state); @@ -72,107 +37,20 @@ public override bool TryWriteCellWithReference(in DataCell cell, StyleId styleId return TryWriteCellWithReference(styleId, state); } - private bool TryWriteCellWithReference(StyleId styleId, CellWriterState state) - { - var buffer = state.Buffer; - var bytes = buffer.GetSpan(); - var written = 0; - - if (!TryWriteCellStartWithReference(state, bytes, ref written)) return false; - if (!"\" t=\"b\" s=\""u8.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(styleId.Id, bytes, ref written)) return false; - if (!TryWriteEndStyleValue(bytes.Slice(written), out var endLength)) return false; - written += endLength; - - buffer.Advance(written); - return true; - } - - public override bool TryWriteCellWithReference(string formulaText, in DataCell cachedValue, StyleId? styleId, DefaultStyling? defaultStyling, CellWriterState state) - { - var buffer = state.Buffer; - var bytes = buffer.GetSpan(); - var written = 0; - - if (styleId is null) - { - if (!TryWriteCellStartWithReference(state, bytes, ref written)) return false; - if (!"\" t=\"b\">"u8.TryCopyTo(bytes, ref written)) return false; - } - else - { - if (!TryWriteCellStartWithReference(state, bytes, ref written)) return false; - if (!"\" t=\"b\" s=\""u8.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(styleId.Id, bytes, ref written)) return false; - if (!FormulaCellHelper.EndStyleBeginFormula.TryCopyTo(bytes, ref written)) return false; - } - - if (!SpanHelper.TryWrite(formulaText, bytes, ref written)) return false; - if (!TryWriteEndFormulaValue(bytes.Slice(written), out var endLength)) return false; - - buffer.Advance(written + endLength); - return true; - } - public override bool TryWriteEndElement(SpreadsheetBuffer buffer) => true; - public override bool TryWriteEndElement(in Cell cell, SpreadsheetBuffer buffer) - { - if (cell.Formula is null) - return true; - - var bytes = buffer.GetSpan(); - if (TryWriteEndFormulaValue(bytes, out var bytesWritten)) - { - buffer.Advance(bytesWritten); - return true; - } - - return false; - } - public override bool WriteFormulaStartElement(StyleId? styleId, DefaultStyling? defaultStyling, SpreadsheetBuffer buffer) { - var bytes = buffer.GetSpan(); - var written = 0; - - if (styleId is null) - { - if (!BeginBooleanFormulaCell.TryCopyTo(bytes, ref written)) return false; - buffer.Advance(written); - return true; - } - - if (!BeginStyledBooleanCell.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(styleId.Id, bytes, ref written)) return false; - if (!FormulaCellHelper.EndStyleBeginFormula.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; + return styleId is { } style + ? buffer.TryWrite($"{BeginStyledBooleanCell}{style.Id}{FormulaCellHelper.EndQuoteBeginFormula}") + : buffer.TryWrite($"{BeginBooleanFormulaCell}"); } public override bool WriteFormulaStartElementWithReference(StyleId? styleId, DefaultStyling? defaultStyling, CellWriterState state) { - var buffer = state.Buffer; - var bytes = buffer.GetSpan(); - var written = 0; - - if (styleId is null) - { - if (!TryWriteCellStartWithReference(state, bytes, ref written)) return false; - if (!"\" t=\"b\">"u8.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; - } - - if (!TryWriteCellStartWithReference(state, bytes, ref written)) return false; - if (!"\" t=\"b\" s=\""u8.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(styleId.Id, bytes, ref written)) return false; - if (!FormulaCellHelper.EndStyleBeginFormula.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; + return styleId is { } style + ? state.Buffer.TryWrite($"{state}{EndReferenceBeginStyled}{style.Id}{FormulaCellHelper.EndQuoteBeginFormula}") + : state.Buffer.TryWrite($"{state}{EndReferenceBeginFormula}"); } public override bool WriteStartElement(SpreadsheetBuffer buffer) => TryWriteCell(buffer); @@ -181,7 +59,7 @@ public override bool WriteFormulaStartElementWithReference(StyleId? styleId, Def public override bool WriteStartElementWithReference(StyleId styleId, CellWriterState state) => TryWriteCellWithReference(styleId, state); /// - /// Returns false because the value is written together with the end element in . + /// Returns false because the value is written together with the end element. /// public override bool CanWriteValuePieceByPiece(in DataCell cell) => false; public override bool WriteValuePieceByPiece(in DataCell cell, SpreadsheetBuffer buffer, ref int valueIndex) => true; diff --git a/SpreadCheetah/CellValueWriters/Boolean/FalseBooleanCellValueWriter.cs b/SpreadCheetah/CellValueWriters/Boolean/FalseBooleanCellValueWriter.cs index 9c5ccfb6..df40441e 100644 --- a/SpreadCheetah/CellValueWriters/Boolean/FalseBooleanCellValueWriter.cs +++ b/SpreadCheetah/CellValueWriters/Boolean/FalseBooleanCellValueWriter.cs @@ -1,59 +1,68 @@ using SpreadCheetah.CellWriters; using SpreadCheetah.Helpers; +using SpreadCheetah.Styling; +using SpreadCheetah.Styling.Internal; namespace SpreadCheetah.CellValueWriters.Boolean; internal sealed class FalseBooleanCellValueWriter : BooleanCellValueWriter { private static ReadOnlySpan FalseBooleanCell => "0"u8; + private static ReadOnlySpan EndReferenceFalseBooleanValue => "\" t=\"b\">0"u8; private static ReadOnlySpan EndStyleFalseBooleanValue => "\">0"u8; private static ReadOnlySpan EndFormulaFalseBooleanValue => "0"u8; protected override bool TryWriteCell(SpreadsheetBuffer buffer) { - var bytes = buffer.GetSpan(); - var written = 0; + return buffer.TryWrite($"{FalseBooleanCell}"); + } + + protected override bool TryWriteCell(StyleId styleId, SpreadsheetBuffer buffer) + { + return buffer.TryWrite($"{BeginStyledBooleanCell}{styleId.Id}{EndStyleFalseBooleanValue}"); + } - if (!FalseBooleanCell.TryCopyTo(bytes, ref written)) return false; + public override bool TryWriteCell(string formulaText, in DataCell cachedValue, StyleId? styleId, DefaultStyling? defaultStyling, SpreadsheetBuffer buffer) + { + if (styleId is { } style) + { + return buffer.TryWrite( + $"{BeginStyledBooleanCell}{style.Id}{FormulaCellHelper.EndQuoteBeginFormula}" + + $"{formulaText}" + + $"{EndFormulaFalseBooleanValue}"); + } - buffer.Advance(written); - return true; + return buffer.TryWrite($"{BeginBooleanFormulaCell}{formulaText}{EndFormulaFalseBooleanValue}"); } protected override bool TryWriteCellWithReference(CellWriterState state) { - var buffer = state.Buffer; - var bytes = buffer.GetSpan(); - var written = 0; - - if (!TryWriteCellStartWithReference(state, bytes, ref written)) return false; - if (!"\" t=\"b\">0"u8.TryCopyTo(bytes, ref written)) return false; + return state.Buffer.TryWrite($"{state}{EndReferenceFalseBooleanValue}"); + } - buffer.Advance(written); - return true; + protected override bool TryWriteCellWithReference(StyleId styleId, CellWriterState state) + { + return state.Buffer.TryWrite($"{state}{EndReferenceBeginStyled}{styleId.Id}{EndStyleFalseBooleanValue}"); } - protected override bool TryWriteEndStyleValue(Span bytes, out int bytesWritten) + public override bool TryWriteCellWithReference(string formulaText, in DataCell cachedValue, StyleId? styleId, DefaultStyling? defaultStyling, CellWriterState state) { - if (EndStyleFalseBooleanValue.TryCopyTo(bytes)) + if (styleId is { } style) { - bytesWritten = EndStyleFalseBooleanValue.Length; - return true; + return state.Buffer.TryWrite( + $"{state}{EndReferenceBeginStyled}{style.Id}{FormulaCellHelper.EndQuoteBeginFormula}" + + $"{formulaText}" + + $"{EndFormulaFalseBooleanValue}"); } - bytesWritten = 0; - return false; + return state.Buffer.TryWrite( + $"{state}{EndReferenceBeginFormula}" + + $"{formulaText}" + + $"{EndFormulaFalseBooleanValue}"); } - protected override bool TryWriteEndFormulaValue(Span bytes, out int bytesWritten) + public override bool TryWriteEndElement(in Cell cell, SpreadsheetBuffer buffer) { - if (EndFormulaFalseBooleanValue.TryCopyTo(bytes)) - { - bytesWritten = EndFormulaFalseBooleanValue.Length; - return true; - } - - bytesWritten = 0; - return false; + return cell.Formula is null || buffer.TryWrite($"{EndFormulaFalseBooleanValue}"); } } diff --git a/SpreadCheetah/CellValueWriters/Boolean/TrueBooleanCellValueWriter.cs b/SpreadCheetah/CellValueWriters/Boolean/TrueBooleanCellValueWriter.cs index 30c51347..2b37d647 100644 --- a/SpreadCheetah/CellValueWriters/Boolean/TrueBooleanCellValueWriter.cs +++ b/SpreadCheetah/CellValueWriters/Boolean/TrueBooleanCellValueWriter.cs @@ -1,59 +1,68 @@ using SpreadCheetah.CellWriters; using SpreadCheetah.Helpers; +using SpreadCheetah.Styling; +using SpreadCheetah.Styling.Internal; namespace SpreadCheetah.CellValueWriters.Boolean; internal sealed class TrueBooleanCellValueWriter : BooleanCellValueWriter { private static ReadOnlySpan TrueBooleanCell => "1"u8; + private static ReadOnlySpan EndReferenceTrueBooleanValue => "\" t=\"b\">1"u8; private static ReadOnlySpan EndStyleTrueBooleanValue => "\">1"u8; private static ReadOnlySpan EndFormulaTrueBooleanValue => "1"u8; protected override bool TryWriteCell(SpreadsheetBuffer buffer) { - var bytes = buffer.GetSpan(); - var written = 0; + return buffer.TryWrite($"{TrueBooleanCell}"); + } + + protected override bool TryWriteCell(StyleId styleId, SpreadsheetBuffer buffer) + { + return buffer.TryWrite($"{BeginStyledBooleanCell}{styleId.Id}{EndStyleTrueBooleanValue}"); + } - if (!TrueBooleanCell.TryCopyTo(bytes, ref written)) return false; + public override bool TryWriteCell(string formulaText, in DataCell cachedValue, StyleId? styleId, DefaultStyling? defaultStyling, SpreadsheetBuffer buffer) + { + if (styleId is { } style) + { + return buffer.TryWrite( + $"{BeginStyledBooleanCell}{style.Id}{FormulaCellHelper.EndQuoteBeginFormula}" + + $"{formulaText}" + + $"{EndFormulaTrueBooleanValue}"); + } - buffer.Advance(written); - return true; + return buffer.TryWrite($"{BeginBooleanFormulaCell}{formulaText}{EndFormulaTrueBooleanValue}"); } protected override bool TryWriteCellWithReference(CellWriterState state) { - var buffer = state.Buffer; - var bytes = buffer.GetSpan(); - var written = 0; - - if (!TryWriteCellStartWithReference(state, bytes, ref written)) return false; - if (!"\" t=\"b\">1"u8.TryCopyTo(bytes, ref written)) return false; + return state.Buffer.TryWrite($"{state}{EndReferenceTrueBooleanValue}"); + } - buffer.Advance(written); - return true; + protected override bool TryWriteCellWithReference(StyleId styleId, CellWriterState state) + { + return state.Buffer.TryWrite($"{state}{EndReferenceBeginStyled}{styleId.Id}{EndStyleTrueBooleanValue}"); } - protected override bool TryWriteEndStyleValue(Span bytes, out int bytesWritten) + public override bool TryWriteCellWithReference(string formulaText, in DataCell cachedValue, StyleId? styleId, DefaultStyling? defaultStyling, CellWriterState state) { - if (EndStyleTrueBooleanValue.TryCopyTo(bytes)) + if (styleId is { } style) { - bytesWritten = EndStyleTrueBooleanValue.Length; - return true; + return state.Buffer.TryWrite( + $"{state}{EndReferenceBeginStyled}{style.Id}{FormulaCellHelper.EndQuoteBeginFormula}" + + $"{formulaText}" + + $"{EndFormulaTrueBooleanValue}"); } - bytesWritten = 0; - return false; + return state.Buffer.TryWrite( + $"{state}{EndReferenceBeginFormula}" + + $"{formulaText}" + + $"{EndFormulaTrueBooleanValue}"); } - protected override bool TryWriteEndFormulaValue(Span bytes, out int bytesWritten) + public override bool TryWriteEndElement(in Cell cell, SpreadsheetBuffer buffer) { - if (EndFormulaTrueBooleanValue.TryCopyTo(bytes)) - { - bytesWritten = EndFormulaTrueBooleanValue.Length; - return true; - } - - bytesWritten = 0; - return false; + return cell.Formula is null || buffer.TryWrite($"{EndFormulaTrueBooleanValue}"); } } diff --git a/SpreadCheetah/CellValueWriters/CellValueWriter.cs b/SpreadCheetah/CellValueWriters/CellValueWriter.cs index 604e4f19..fb367de9 100644 --- a/SpreadCheetah/CellValueWriters/CellValueWriter.cs +++ b/SpreadCheetah/CellValueWriters/CellValueWriter.cs @@ -2,7 +2,6 @@ using SpreadCheetah.CellValueWriters.Number; using SpreadCheetah.CellValueWriters.Time; using SpreadCheetah.CellWriters; -using SpreadCheetah.Helpers; using SpreadCheetah.Styling; using SpreadCheetah.Styling.Internal; @@ -36,15 +35,4 @@ internal abstract class CellValueWriter public abstract bool WriteValuePieceByPiece(in DataCell cell, SpreadsheetBuffer buffer, ref int valueIndex); public abstract bool TryWriteEndElement(SpreadsheetBuffer buffer); public abstract bool TryWriteEndElement(in Cell cell, SpreadsheetBuffer buffer); - - protected static bool TryWriteCellStartWithReference(CellWriterState state, Span bytes, ref int bytesWritten) - { - var written = 0; - - if (!" NullDataCell => ""u8; - private static ReadOnlySpan EndStyleNullValue => "\"/>"u8; + private static ReadOnlySpan EndQuoteNullValue => "\"/>"u8; private static ReadOnlySpan EndFormulaEndCell => ""u8; protected static bool TryWriteCell(SpreadsheetBuffer buffer) { - var bytes = buffer.GetSpan(); - var written = 0; - - if (!NullDataCell.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; + return buffer.TryWrite($"{NullDataCell}"); } protected static bool TryWriteCell(int styleId, SpreadsheetBuffer buffer) { - var bytes = buffer.GetSpan(); - var written = 0; - - if (!StyledCellHelper.BeginStyledNumberCell.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(styleId, bytes, ref written)) return false; - if (!EndStyleNullValue.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; + return buffer.TryWrite($"{StyledCellHelper.BeginStyledNumberCell}{styleId}{EndQuoteNullValue}"); } protected static bool TryWriteCell(string formulaText, int? styleId, SpreadsheetBuffer buffer) { - var bytes = buffer.GetSpan(); - - if (!NumberCellValueWriterBase.TryWriteFormulaCellStart(styleId, bytes, out var written)) return false; - if (!SpanHelper.TryWrite(formulaText, bytes, ref written)) return false; - if (!EndFormulaEndCell.TryCopyTo(bytes, ref written)) return false; + if (styleId is { } style) + { + return buffer.TryWrite( + $"{StyledCellHelper.BeginStyledNumberCell}{style}{FormulaCellHelper.EndQuoteBeginFormula}" + + $"{formulaText}" + + $"{EndFormulaEndCell}"); + } - buffer.Advance(written); - return true; + return buffer.TryWrite( + $"{FormulaCellHelper.BeginNumberFormulaCell}" + + $"{formulaText}" + + $"{EndFormulaEndCell}"); } public override bool TryWriteCell(in DataCell cell, StyleId styleId, SpreadsheetBuffer buffer) @@ -56,15 +45,7 @@ public override bool TryWriteCell(in DataCell cell, StyleId styleId, Spreadsheet protected static bool TryWriteCellWithReference(CellWriterState state) { - var buffer = state.Buffer; - var bytes = buffer.GetSpan(); - var written = 0; - - if (!TryWriteCellStartWithReference(state, bytes, ref written)) return false; - if (!"\"/>"u8.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; + return state.Buffer.TryWrite($"{state}{EndQuoteNullValue}"); } public override bool TryWriteCellWithReference(in DataCell cell, StyleId styleId, CellWriterState state) @@ -74,91 +55,44 @@ public override bool TryWriteCellWithReference(in DataCell cell, StyleId styleId protected static bool TryWriteCellWithReference(int styleId, CellWriterState state) { - var buffer = state.Buffer; - var bytes = buffer.GetSpan(); - var written = 0; - - if (!TryWriteCellStartWithReference(state, bytes, ref written)) return false; - if (!StyledCellHelper.EndReferenceBeginStyleId.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(styleId, bytes, ref written)) return false; - if (!EndStyleNullValue.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; + return state.Buffer.TryWrite($"{state}{StyledCellHelper.EndReferenceBeginStyleId}{styleId}{EndQuoteNullValue}"); } protected static bool TryWriteCellWithReference(string formulaText, int? styleId, CellWriterState state) { - var buffer = state.Buffer; - var bytes = buffer.GetSpan(); - - if (!NumberCellValueWriterBase.TryWriteFormulaCellStartWithReference(styleId, state, bytes, out var written)) return false; - if (!SpanHelper.TryWrite(formulaText, bytes, ref written)) return false; - if (!EndFormulaEndCell.TryCopyTo(bytes, ref written)) return false; + if (styleId is { } style) + { + return state.Buffer.TryWrite( + $"{state}{StyledCellHelper.EndReferenceBeginStyleId}{style}{FormulaCellHelper.EndQuoteBeginFormula}" + + $"{formulaText}" + + $"{EndFormulaEndCell}"); + } - buffer.Advance(written); - return true; + return state.Buffer.TryWrite( + $"{state}{FormulaCellHelper.EndQuoteBeginFormula}" + + $"{formulaText}" + + $"{EndFormulaEndCell}"); } public override bool TryWriteEndElement(SpreadsheetBuffer buffer) => true; public override bool TryWriteEndElement(in Cell cell, SpreadsheetBuffer buffer) { - if (cell.Formula is null) - return true; - - var bytes = buffer.GetSpan(); - if (EndFormulaEndCell.TryCopyTo(bytes)) - { - buffer.Advance(EndFormulaEndCell.Length); - return true; - } - - return false; + return cell.Formula is null || buffer.TryWrite($"{EndFormulaEndCell}"); } protected static bool WriteFormulaStartElement(int? styleId, SpreadsheetBuffer buffer) { - var bytes = buffer.GetSpan(); - var written = 0; - - if (styleId is null) - { - if (!FormulaCellHelper.BeginNumberFormulaCell.TryCopyTo(bytes, ref written)) return false; - buffer.Advance(written); - return true; - } - - if (!StyledCellHelper.BeginStyledNumberCell.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(styleId.Value, bytes, ref written)) return false; - if (!FormulaCellHelper.EndStyleBeginFormula.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; + return styleId is { } style + ? buffer.TryWrite($"{StyledCellHelper.BeginStyledNumberCell}{style}{FormulaCellHelper.EndQuoteBeginFormula}") + : buffer.TryWrite($"{FormulaCellHelper.BeginNumberFormulaCell}"); } protected static bool WriteFormulaStartElementWithReference(int? styleId, CellWriterState state) { - var buffer = state.Buffer; - var bytes = buffer.GetSpan(); - var written = 0; - - if (styleId is null) - { - if (!TryWriteCellStartWithReference(state, bytes, ref written)) return false; - if (!"\">"u8.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; - } - - if (!TryWriteCellStartWithReference(state, bytes, ref written)) return false; - if (!StyledCellHelper.EndReferenceBeginStyleId.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(styleId.Value, bytes, ref written)) return false; - if (!FormulaCellHelper.EndStyleBeginFormula.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; + return styleId is { } style + ? state.Buffer.TryWrite($"{state}{StyledCellHelper.EndReferenceBeginStyleId}{style}{FormulaCellHelper.EndQuoteBeginFormula}") + : state.Buffer.TryWrite($"{state}{FormulaCellHelper.EndQuoteBeginFormula}"); } public override bool WriteStartElement(SpreadsheetBuffer buffer) => TryWriteCell(buffer); diff --git a/SpreadCheetah/CellValueWriters/Number/DoubleCellValueWriter.cs b/SpreadCheetah/CellValueWriters/Number/DoubleCellValueWriter.cs index c587afa6..1bf99c7a 100644 --- a/SpreadCheetah/CellValueWriters/Number/DoubleCellValueWriter.cs +++ b/SpreadCheetah/CellValueWriters/Number/DoubleCellValueWriter.cs @@ -1,11 +1,83 @@ -using System.Buffers.Text; +using SpreadCheetah.CellWriters; +using SpreadCheetah.Helpers; +using SpreadCheetah.Styling; +using SpreadCheetah.Styling.Internal; namespace SpreadCheetah.CellValueWriters.Number; internal sealed class DoubleCellValueWriter : NumberCellValueWriter { - protected override bool TryWriteValue(in DataCell cell, Span destination, out int bytesWritten) + public override bool TryWriteCell(in DataCell cell, DefaultStyling? defaultStyling, SpreadsheetBuffer buffer) { - return Utf8Formatter.TryFormat(cell.NumberValue.DoubleValue, destination, out bytesWritten); + return buffer.TryWrite($"{BeginDataCell}{cell.NumberValue.DoubleValue}{EndDefaultCell}"); + } + + public override bool TryWriteCell(in DataCell cell, StyleId styleId, SpreadsheetBuffer buffer) + { + return buffer.TryWrite( + $"{StyledCellHelper.BeginStyledNumberCell}{styleId.Id}{EndQuoteBeginValue}" + + $"{cell.NumberValue.DoubleValue}" + + $"{EndDefaultCell}"); + } + + public override bool TryWriteCell(string formulaText, in DataCell cachedValue, StyleId? styleId, DefaultStyling? defaultStyling, SpreadsheetBuffer buffer) + { + if (styleId is { } style) + { + return buffer.TryWrite( + $"{StyledCellHelper.BeginStyledNumberCell}{style.Id}{FormulaCellHelper.EndQuoteBeginFormula}" + + $"{formulaText}" + + $"{FormulaCellHelper.EndFormulaBeginCachedValue}" + + $"{cachedValue.NumberValue.DoubleValue}" + + $"{FormulaCellHelper.EndCachedValueEndCell}"); + } + + return buffer.TryWrite( + $"{FormulaCellHelper.BeginNumberFormulaCell}" + + $"{formulaText}" + + $"{FormulaCellHelper.EndFormulaBeginCachedValue}" + + $"{cachedValue.NumberValue.DoubleValue}" + + $"{FormulaCellHelper.EndCachedValueEndCell}"); + } + + public override bool TryWriteCellWithReference(in DataCell cell, DefaultStyling? defaultStyling, CellWriterState state) + { + return state.Buffer.TryWrite( + $"{state}{EndQuoteBeginValue}" + + $"{cell.NumberValue.DoubleValue}" + + $"{EndDefaultCell}"); + } + + public override bool TryWriteCellWithReference(in DataCell cell, StyleId styleId, CellWriterState state) + { + return state.Buffer.TryWrite( + $"{state}{StyledCellHelper.EndReferenceBeginStyleId}{styleId.Id}{EndQuoteBeginValue}" + + $"{cell.NumberValue.DoubleValue}" + + $"{EndDefaultCell}"); + } + + public override bool TryWriteCellWithReference(string formulaText, in DataCell cachedValue, StyleId? styleId, DefaultStyling? defaultStyling, CellWriterState state) + { + if (styleId is { } style) + { + return state.Buffer.TryWrite( + $"{state}{StyledCellHelper.EndReferenceBeginStyleId}{style.Id}{FormulaCellHelper.EndQuoteBeginFormula}" + + $"{formulaText}" + + $"{FormulaCellHelper.EndFormulaBeginCachedValue}" + + $"{cachedValue.NumberValue.DoubleValue}" + + $"{FormulaCellHelper.EndCachedValueEndCell}"); + } + + return state.Buffer.TryWrite( + $"{state}{FormulaCellHelper.EndQuoteBeginFormula}" + + $"{formulaText}" + + $"{FormulaCellHelper.EndFormulaBeginCachedValue}" + + $"{cachedValue.NumberValue.DoubleValue}" + + $"{FormulaCellHelper.EndCachedValueEndCell}"); + } + + public override bool WriteValuePieceByPiece(in DataCell cell, SpreadsheetBuffer buffer, ref int valueIndex) + { + return buffer.TryWrite($"{cell.NumberValue.DoubleValue}"); } } diff --git a/SpreadCheetah/CellValueWriters/Number/FloatCellValueWriter.cs b/SpreadCheetah/CellValueWriters/Number/FloatCellValueWriter.cs index 721de7e6..69b08ab7 100644 --- a/SpreadCheetah/CellValueWriters/Number/FloatCellValueWriter.cs +++ b/SpreadCheetah/CellValueWriters/Number/FloatCellValueWriter.cs @@ -1,11 +1,83 @@ -using System.Buffers.Text; +using SpreadCheetah.CellWriters; +using SpreadCheetah.Helpers; +using SpreadCheetah.Styling; +using SpreadCheetah.Styling.Internal; namespace SpreadCheetah.CellValueWriters.Number; internal sealed class FloatCellValueWriter : NumberCellValueWriter { - protected override bool TryWriteValue(in DataCell cell, Span destination, out int bytesWritten) + public override bool TryWriteCell(in DataCell cell, DefaultStyling? defaultStyling, SpreadsheetBuffer buffer) { - return Utf8Formatter.TryFormat(cell.NumberValue.FloatValue, destination, out bytesWritten); + return buffer.TryWrite($"{BeginDataCell}{cell.NumberValue.FloatValue}{EndDefaultCell}"); + } + + public override bool TryWriteCell(in DataCell cell, StyleId styleId, SpreadsheetBuffer buffer) + { + return buffer.TryWrite( + $"{StyledCellHelper.BeginStyledNumberCell}{styleId.Id}{EndQuoteBeginValue}" + + $"{cell.NumberValue.FloatValue}" + + $"{EndDefaultCell}"); + } + + public override bool TryWriteCell(string formulaText, in DataCell cachedValue, StyleId? styleId, DefaultStyling? defaultStyling, SpreadsheetBuffer buffer) + { + if (styleId is { } style) + { + return buffer.TryWrite( + $"{StyledCellHelper.BeginStyledNumberCell}{style.Id}{FormulaCellHelper.EndQuoteBeginFormula}" + + $"{formulaText}" + + $"{FormulaCellHelper.EndFormulaBeginCachedValue}" + + $"{cachedValue.NumberValue.FloatValue}" + + $"{FormulaCellHelper.EndCachedValueEndCell}"); + } + + return buffer.TryWrite( + $"{FormulaCellHelper.BeginNumberFormulaCell}" + + $"{formulaText}" + + $"{FormulaCellHelper.EndFormulaBeginCachedValue}" + + $"{cachedValue.NumberValue.FloatValue}" + + $"{FormulaCellHelper.EndCachedValueEndCell}"); + } + + public override bool TryWriteCellWithReference(in DataCell cell, DefaultStyling? defaultStyling, CellWriterState state) + { + return state.Buffer.TryWrite( + $"{state}{EndQuoteBeginValue}" + + $"{cell.NumberValue.FloatValue}" + + $"{EndDefaultCell}"); + } + + public override bool TryWriteCellWithReference(in DataCell cell, StyleId styleId, CellWriterState state) + { + return state.Buffer.TryWrite( + $"{state}{StyledCellHelper.EndReferenceBeginStyleId}{styleId.Id}{EndQuoteBeginValue}" + + $"{cell.NumberValue.FloatValue}" + + $"{EndDefaultCell}"); + } + + public override bool TryWriteCellWithReference(string formulaText, in DataCell cachedValue, StyleId? styleId, DefaultStyling? defaultStyling, CellWriterState state) + { + if (styleId is { } style) + { + return state.Buffer.TryWrite( + $"{state}{StyledCellHelper.EndReferenceBeginStyleId}{style.Id}{FormulaCellHelper.EndQuoteBeginFormula}" + + $"{formulaText}" + + $"{FormulaCellHelper.EndFormulaBeginCachedValue}" + + $"{cachedValue.NumberValue.FloatValue}" + + $"{FormulaCellHelper.EndCachedValueEndCell}"); + } + + return state.Buffer.TryWrite( + $"{state}{FormulaCellHelper.EndQuoteBeginFormula}" + + $"{formulaText}" + + $"{FormulaCellHelper.EndFormulaBeginCachedValue}" + + $"{cachedValue.NumberValue.FloatValue}" + + $"{FormulaCellHelper.EndCachedValueEndCell}"); + } + + public override bool WriteValuePieceByPiece(in DataCell cell, SpreadsheetBuffer buffer, ref int valueIndex) + { + return buffer.TryWrite($"{cell.NumberValue.FloatValue}"); } } diff --git a/SpreadCheetah/CellValueWriters/Number/IntegerCellValueWriter.cs b/SpreadCheetah/CellValueWriters/Number/IntegerCellValueWriter.cs index 097161ac..8f914b0a 100644 --- a/SpreadCheetah/CellValueWriters/Number/IntegerCellValueWriter.cs +++ b/SpreadCheetah/CellValueWriters/Number/IntegerCellValueWriter.cs @@ -1,11 +1,83 @@ -using System.Buffers.Text; +using SpreadCheetah.CellWriters; +using SpreadCheetah.Helpers; +using SpreadCheetah.Styling; +using SpreadCheetah.Styling.Internal; namespace SpreadCheetah.CellValueWriters.Number; internal sealed class IntegerCellValueWriter : NumberCellValueWriter { - protected override bool TryWriteValue(in DataCell cell, Span destination, out int bytesWritten) + public override bool TryWriteCell(in DataCell cell, DefaultStyling? defaultStyling, SpreadsheetBuffer buffer) { - return Utf8Formatter.TryFormat(cell.NumberValue.IntValue, destination, out bytesWritten); + return buffer.TryWrite($"{BeginDataCell}{cell.NumberValue.IntValue}{EndDefaultCell}"); + } + + public override bool TryWriteCell(in DataCell cell, StyleId styleId, SpreadsheetBuffer buffer) + { + return buffer.TryWrite( + $"{StyledCellHelper.BeginStyledNumberCell}{styleId.Id}{EndQuoteBeginValue}" + + $"{cell.NumberValue.IntValue}" + + $"{EndDefaultCell}"); + } + + public override bool TryWriteCell(string formulaText, in DataCell cachedValue, StyleId? styleId, DefaultStyling? defaultStyling, SpreadsheetBuffer buffer) + { + if (styleId is { } style) + { + return buffer.TryWrite( + $"{StyledCellHelper.BeginStyledNumberCell}{style.Id}{FormulaCellHelper.EndQuoteBeginFormula}" + + $"{formulaText}" + + $"{FormulaCellHelper.EndFormulaBeginCachedValue}" + + $"{cachedValue.NumberValue.IntValue}" + + $"{FormulaCellHelper.EndCachedValueEndCell}"); + } + + return buffer.TryWrite( + $"{FormulaCellHelper.BeginNumberFormulaCell}" + + $"{formulaText}" + + $"{FormulaCellHelper.EndFormulaBeginCachedValue}" + + $"{cachedValue.NumberValue.IntValue}" + + $"{FormulaCellHelper.EndCachedValueEndCell}"); + } + + public override bool TryWriteCellWithReference(in DataCell cell, DefaultStyling? defaultStyling, CellWriterState state) + { + return state.Buffer.TryWrite( + $"{state}{EndQuoteBeginValue}" + + $"{cell.NumberValue.IntValue}" + + $"{EndDefaultCell}"); + } + + public override bool TryWriteCellWithReference(in DataCell cell, StyleId styleId, CellWriterState state) + { + return state.Buffer.TryWrite( + $"{state}{StyledCellHelper.EndReferenceBeginStyleId}{styleId.Id}{EndQuoteBeginValue}" + + $"{cell.NumberValue.IntValue}" + + $"{EndDefaultCell}"); + } + + public override bool TryWriteCellWithReference(string formulaText, in DataCell cachedValue, StyleId? styleId, DefaultStyling? defaultStyling, CellWriterState state) + { + if (styleId is { } style) + { + return state.Buffer.TryWrite( + $"{state}{StyledCellHelper.EndReferenceBeginStyleId}{style.Id}{FormulaCellHelper.EndQuoteBeginFormula}" + + $"{formulaText}" + + $"{FormulaCellHelper.EndFormulaBeginCachedValue}" + + $"{cachedValue.NumberValue.IntValue}" + + $"{FormulaCellHelper.EndCachedValueEndCell}"); + } + + return state.Buffer.TryWrite( + $"{state}{FormulaCellHelper.EndQuoteBeginFormula}" + + $"{formulaText}" + + $"{FormulaCellHelper.EndFormulaBeginCachedValue}" + + $"{cachedValue.NumberValue.IntValue}" + + $"{FormulaCellHelper.EndCachedValueEndCell}"); + } + + public override bool WriteValuePieceByPiece(in DataCell cell, SpreadsheetBuffer buffer, ref int valueIndex) + { + return buffer.TryWrite($"{cell.NumberValue.IntValue}"); } } diff --git a/SpreadCheetah/CellValueWriters/Number/NumberCellValueWriter.cs b/SpreadCheetah/CellValueWriters/Number/NumberCellValueWriter.cs index ec4eb6cc..f55552b1 100644 --- a/SpreadCheetah/CellValueWriters/Number/NumberCellValueWriter.cs +++ b/SpreadCheetah/CellValueWriters/Number/NumberCellValueWriter.cs @@ -8,26 +8,6 @@ internal abstract class NumberCellValueWriter : NumberCellValueWriterBase { protected override int GetStyleId(StyleId styleId) => styleId.Id; - public override bool TryWriteCell(in DataCell cell, DefaultStyling? defaultStyling, SpreadsheetBuffer buffer) - { - return TryWriteCell(cell, buffer); - } - - public override bool TryWriteCell(string formulaText, in DataCell cachedValue, StyleId? styleId, DefaultStyling? defaultStyling, SpreadsheetBuffer buffer) - { - return TryWriteCell(formulaText, cachedValue, styleId?.Id, buffer); - } - - public override bool TryWriteCellWithReference(in DataCell cell, DefaultStyling? defaultStyling, CellWriterState state) - { - return TryWriteCellWithReference(cell, state); - } - - public override bool TryWriteCellWithReference(string formulaText, in DataCell cachedValue, StyleId? styleId, DefaultStyling? defaultStyling, CellWriterState state) - { - return TryWriteCellWithReference(formulaText, cachedValue, styleId?.Id, state); - } - public override bool WriteFormulaStartElement(StyleId? styleId, DefaultStyling? defaultStyling, SpreadsheetBuffer buffer) { return WriteFormulaStartElement(styleId?.Id, buffer); diff --git a/SpreadCheetah/CellValueWriters/Number/NumberCellValueWriterBase.cs b/SpreadCheetah/CellValueWriters/Number/NumberCellValueWriterBase.cs index 01659e39..b2de20c3 100644 --- a/SpreadCheetah/CellValueWriters/Number/NumberCellValueWriterBase.cs +++ b/SpreadCheetah/CellValueWriters/Number/NumberCellValueWriterBase.cs @@ -7,294 +7,58 @@ namespace SpreadCheetah.CellValueWriters.Number; internal abstract class NumberCellValueWriterBase : CellValueWriter { protected abstract int GetStyleId(StyleId styleId); - protected abstract bool TryWriteValue(in DataCell cell, Span destination, out int bytesWritten); - private static ReadOnlySpan BeginDataCell => ""u8; - private static ReadOnlySpan EndStyleBeginValue => "\">"u8; - private static ReadOnlySpan EndDefaultCell => ""u8; - - protected bool TryWriteCell(in DataCell cell, SpreadsheetBuffer buffer) - { - var bytes = buffer.GetSpan(); - var written = 0; - - if (!BeginDataCell.TryCopyTo(bytes, ref written)) return false; - if (!TryWriteValue(cell, bytes.Slice(written), out var valueLength)) return false; - written += valueLength; - - if (!EndDefaultCell.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; - } - - protected bool TryWriteCell(in DataCell cell, int styleId, SpreadsheetBuffer buffer) - { - var bytes = buffer.GetSpan(); - var written = 0; - - if (!StyledCellHelper.BeginStyledNumberCell.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(styleId, bytes, ref written)) return false; - if (!EndStyleBeginValue.TryCopyTo(bytes, ref written)) return false; - - if (!TryWriteValue(cell, bytes.Slice(written), out var valueLength)) return false; - written += valueLength; - - if (!EndDefaultCell.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; - } - - protected bool TryWriteCell(string formulaText, in DataCell cachedValue, int? styleId, SpreadsheetBuffer buffer) - { - var bytes = buffer.GetSpan(); - - if (!TryWriteFormulaCellStart(styleId, bytes, out var written)) return false; - if (!SpanHelper.TryWrite(formulaText, bytes, ref written)) return false; - if (!FormulaCellHelper.EndFormulaBeginCachedValue.TryCopyTo(bytes, ref written)) return false; - if (!TryWriteValue(cachedValue, bytes.Slice(written), out var valueLength)) return false; - written += valueLength; - - if (!FormulaCellHelper.EndCachedValueEndCell.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; - } - - public override bool TryWriteCell(in DataCell cell, StyleId styleId, SpreadsheetBuffer buffer) - { - return TryWriteCell(cell, GetStyleId(styleId), buffer); - } - - public override bool TryWriteCellWithReference(in DataCell cell, StyleId styleId, CellWriterState state) - { - return TryWriteCellWithReference(cell, GetStyleId(styleId), state); - } - - protected bool TryWriteCellWithReference(in DataCell cell, CellWriterState state) - { - var buffer = state.Buffer; - var bytes = buffer.GetSpan(); - var written = 0; - - if (!TryWriteCellStartWithReference(state, bytes, ref written)) return false; - if (!"\">"u8.TryCopyTo(bytes, ref written)) return false; - if (!TryWriteValue(cell, bytes.Slice(written), out var valueLength)) return false; - written += valueLength; - - if (!EndDefaultCell.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; - } - - protected bool TryWriteCellWithReference(in DataCell cell, int styleId, CellWriterState state) - { - var buffer = state.Buffer; - var bytes = buffer.GetSpan(); - var written = 0; - - if (!TryWriteCellStartWithReference(state, bytes, ref written)) return false; - if (!StyledCellHelper.EndReferenceBeginStyleId.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(styleId, bytes, ref written)) return false; - if (!EndStyleBeginValue.TryCopyTo(bytes, ref written)) return false; - - if (!TryWriteValue(cell, bytes.Slice(written), out var valueLength)) return false; - written += valueLength; - - if (!EndDefaultCell.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; - } - - protected bool TryWriteCellWithReference(string formulaText, in DataCell cachedValue, int? styleId, CellWriterState state) - { - var buffer = state.Buffer; - var bytes = buffer.GetSpan(); - - if (!TryWriteFormulaCellStartWithReference(styleId, state, bytes, out var written)) return false; - if (!SpanHelper.TryWrite(formulaText, bytes, ref written)) return false; - if (!FormulaCellHelper.EndFormulaBeginCachedValue.TryCopyTo(bytes, ref written)) return false; - if (!TryWriteValue(cachedValue, bytes.Slice(written), out var valueLength)) return false; - written += valueLength; - - if (!FormulaCellHelper.EndCachedValueEndCell.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; - } - - public static bool TryWriteFormulaCellStart(int? styleId, Span bytes, out int bytesWritten) - { - bytesWritten = 0; - var written = 0; - - if (styleId is null) - { - return FormulaCellHelper.BeginNumberFormulaCell.TryCopyTo(bytes, ref bytesWritten); - } - - if (!StyledCellHelper.BeginStyledNumberCell.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(styleId.Value, bytes, ref written)) return false; - if (!FormulaCellHelper.EndStyleBeginFormula.TryCopyTo(bytes, ref written)) return false; - - bytesWritten = written; - return true; - } - - public static bool TryWriteFormulaCellStartWithReference(int? styleId, CellWriterState state, Span bytes, out int bytesWritten) - { - bytesWritten = 0; - var written = 0; - - if (styleId is null) - { - if (!TryWriteCellStartWithReference(state, bytes, ref written)) return false; - if (!"\">"u8.TryCopyTo(bytes, ref written)) return false; - - bytesWritten = written; - return true; - } - - if (!TryWriteCellStartWithReference(state, bytes, ref written)) return false; - if (!StyledCellHelper.EndReferenceBeginStyleId.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(styleId.Value, bytes, ref written)) return false; - if (!FormulaCellHelper.EndStyleBeginFormula.TryCopyTo(bytes, ref written)) return false; - - bytesWritten = written; - return true; - } + protected static ReadOnlySpan BeginDataCell => ""u8; + protected static ReadOnlySpan EndQuoteBeginValue => "\">"u8; + protected static ReadOnlySpan EndDefaultCell => ""u8; public override bool WriteStartElement(SpreadsheetBuffer buffer) { - var bytes = buffer.GetSpan(); - var written = 0; - - if (!BeginDataCell.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; + return buffer.TryWrite($"{BeginDataCell}"); } public override bool WriteStartElement(StyleId styleId, SpreadsheetBuffer buffer) { - var bytes = buffer.GetSpan(); - var written = 0; - - if (!StyledCellHelper.BeginStyledNumberCell.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(GetStyleId(styleId), bytes, ref written)) return false; - if (!EndStyleBeginValue.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; + var style = GetStyleId(styleId); + return buffer.TryWrite($"{StyledCellHelper.BeginStyledNumberCell}{style}{EndQuoteBeginValue}"); } public override bool WriteStartElementWithReference(CellWriterState state) { - var buffer = state.Buffer; - var bytes = buffer.GetSpan(); - var written = 0; - - if (!TryWriteCellStartWithReference(state, bytes, ref written)) return false; - if (!"\">"u8.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; + return state.Buffer.TryWrite($"{state}{EndQuoteBeginValue}"); } public override bool WriteStartElementWithReference(StyleId styleId, CellWriterState state) { - var buffer = state.Buffer; - var bytes = buffer.GetSpan(); - var written = 0; - - if (!TryWriteCellStartWithReference(state, bytes, ref written)) return false; - if (!StyledCellHelper.EndReferenceBeginStyleId.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(GetStyleId(styleId), bytes, ref written)) return false; - if (!EndStyleBeginValue.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; + var style = GetStyleId(styleId); + return state.Buffer.TryWrite($"{state}{StyledCellHelper.EndReferenceBeginStyleId}{style}{EndQuoteBeginValue}"); } protected static bool WriteFormulaStartElement(int? styleId, SpreadsheetBuffer buffer) { - var bytes = buffer.GetSpan(); - var written = 0; - - if (styleId is null) - { - if (!FormulaCellHelper.BeginNumberFormulaCell.TryCopyTo(bytes, ref written)) return false; - buffer.Advance(written); - return true; - } - - if (!StyledCellHelper.BeginStyledNumberCell.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(styleId.Value, bytes, ref written)) return false; - if (!FormulaCellHelper.EndStyleBeginFormula.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; + return styleId is { } style + ? buffer.TryWrite($"{StyledCellHelper.BeginStyledNumberCell}{style}{FormulaCellHelper.EndQuoteBeginFormula}") + : buffer.TryWrite($"{FormulaCellHelper.BeginNumberFormulaCell}"); } protected static bool WriteFormulaStartElementWithReference(int? styleId, CellWriterState state) { - var buffer = state.Buffer; - var bytes = buffer.GetSpan(); - var written = 0; - - if (styleId is null) - { - if (!TryWriteCellStartWithReference(state, bytes, ref written)) return false; - if (!"\">"u8.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; - } - - if (!TryWriteCellStartWithReference(state, bytes, ref written)) return false; - if (!StyledCellHelper.EndReferenceBeginStyleId.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(styleId.Value, bytes, ref written)) return false; - if (!FormulaCellHelper.EndStyleBeginFormula.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; + return styleId is { } style + ? state.Buffer.TryWrite($"{state}{StyledCellHelper.EndReferenceBeginStyleId}{style}{FormulaCellHelper.EndQuoteBeginFormula}") + : state.Buffer.TryWrite($"{state}{FormulaCellHelper.EndQuoteBeginFormula}"); } public override bool CanWriteValuePieceByPiece(in DataCell cell) => true; - public override bool WriteValuePieceByPiece(in DataCell cell, SpreadsheetBuffer buffer, ref int valueIndex) - { - var bytes = buffer.GetSpan(); - if (!TryWriteValue(cell, bytes, out var bytesWritten)) - return false; - - buffer.Advance(bytesWritten); - return true; - } - public override bool TryWriteEndElement(SpreadsheetBuffer buffer) { - var bytes = buffer.GetSpan(); - if (!EndDefaultCell.TryCopyTo(bytes)) - return false; - - buffer.Advance(EndDefaultCell.Length); - return true; + return buffer.TryWrite($"{EndDefaultCell}"); } public override bool TryWriteEndElement(in Cell cell, SpreadsheetBuffer buffer) { - if (cell.Formula is null) - return TryWriteEndElement(buffer); - - var bytes = buffer.GetSpan(); - if (!FormulaCellHelper.EndCachedValueEndCell.TryCopyTo(bytes)) - return false; - - buffer.Advance(FormulaCellHelper.EndCachedValueEndCell.Length); - return true; + return cell.Formula is null + ? TryWriteEndElement(buffer) + : buffer.TryWrite($"{FormulaCellHelper.EndCachedValueEndCell}"); } } diff --git a/SpreadCheetah/CellValueWriters/StringCellValueWriter.cs b/SpreadCheetah/CellValueWriters/StringCellValueWriter.cs index 8f1f3e59..341029a5 100644 --- a/SpreadCheetah/CellValueWriters/StringCellValueWriter.cs +++ b/SpreadCheetah/CellValueWriters/StringCellValueWriter.cs @@ -11,239 +11,123 @@ internal sealed class StringCellValueWriter : CellValueWriter private static ReadOnlySpan BeginStyledStringCell => " BeginStringFormulaCell => ""u8; private static ReadOnlySpan BeginStyledStringFormulaCell => " EndReferenceBeginString => "\" t=\"inlineStr\">"u8; + private static ReadOnlySpan EndReferenceBeginStyle => "\" t=\"inlineStr\" s=\""u8; + private static ReadOnlySpan EndReferenceBeginFormula => "\" t=\"str\">"u8; + private static ReadOnlySpan EndReferenceBeginFormulaCellStyle => "\" t=\"str\" s=\""u8; private static ReadOnlySpan EndStyleBeginInlineString => "\">"u8; private static ReadOnlySpan EndStringCell => ""u8; public override bool TryWriteCell(in DataCell cell, DefaultStyling? defaultStyling, SpreadsheetBuffer buffer) { - var bytes = buffer.GetSpan(); - var written = 0; - - if (!BeginStringCell.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(cell.StringValue, bytes, ref written)) return false; - if (!EndStringCell.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; + return buffer.TryWrite($"{BeginStringCell}{cell.StringValue}{EndStringCell}"); } public override bool TryWriteCell(in DataCell cell, StyleId styleId, SpreadsheetBuffer buffer) { - var bytes = buffer.GetSpan(); - var written = 0; - - if (!BeginStyledStringCell.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(styleId.Id, bytes, ref written)) return false; - if (!EndStyleBeginInlineString.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(cell.StringValue, bytes, ref written)) return false; - if (!EndStringCell.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; + return buffer.TryWrite( + $"{BeginStyledStringCell}{styleId.Id}{EndStyleBeginInlineString}" + + $"{cell.StringValue}" + + $"{EndStringCell}"); } public override bool TryWriteCell(string formulaText, in DataCell cachedValue, StyleId? styleId, DefaultStyling? defaultStyling, SpreadsheetBuffer buffer) { - var bytes = buffer.GetSpan(); - var written = 0; - - if (styleId is null) + if (styleId is { } style) { - if (!BeginStringFormulaCell.TryCopyTo(bytes, ref written)) return false; - } - else - { - if (!BeginStyledStringFormulaCell.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(styleId.Id, bytes, ref written)) return false; - if (!FormulaCellHelper.EndStyleBeginFormula.TryCopyTo(bytes, ref written)) return false; + return buffer.TryWrite( + $"{BeginStyledStringFormulaCell}{style.Id}{FormulaCellHelper.EndQuoteBeginFormula}" + + $"{formulaText}" + + $"{FormulaCellHelper.EndFormulaBeginCachedValue}" + + $"{cachedValue.StringValue}" + + $"{FormulaCellHelper.EndCachedValueEndCell}"); } - if (!SpanHelper.TryWrite(formulaText, bytes, ref written)) return false; - if (!FormulaCellHelper.EndFormulaBeginCachedValue.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(cachedValue.StringValue, bytes, ref written)) return false; - if (!FormulaCellHelper.EndCachedValueEndCell.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; + return buffer.TryWrite( + $"{BeginStringFormulaCell}" + + $"{formulaText}" + + $"{FormulaCellHelper.EndFormulaBeginCachedValue}" + + $"{cachedValue.StringValue}" + + $"{FormulaCellHelper.EndCachedValueEndCell}"); } public override bool TryWriteCellWithReference(in DataCell cell, DefaultStyling? defaultStyling, CellWriterState state) { - var buffer = state.Buffer; - var bytes = buffer.GetSpan(); - var written = 0; - - if (!TryWriteCellStartWithReference(state, bytes, ref written)) return false; - if (!"\" t=\"inlineStr\">"u8.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(cell.StringValue, bytes, ref written)) return false; - if (!EndStringCell.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; + return state.Buffer.TryWrite($"{state}{EndReferenceBeginString}{cell.StringValue}{EndStringCell}"); } public override bool TryWriteCellWithReference(in DataCell cell, StyleId styleId, CellWriterState state) { - var buffer = state.Buffer; - var bytes = buffer.GetSpan(); - var written = 0; - - if (!TryWriteCellStartWithReference(state, bytes, ref written)) return false; - if (!"\" t=\"inlineStr\" s=\""u8.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(styleId.Id, bytes, ref written)) return false; - if (!EndStyleBeginInlineString.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(cell.StringValue, bytes, ref written)) return false; - if (!EndStringCell.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; + return state.Buffer.TryWrite( + $"{state}{EndReferenceBeginStyle}{styleId.Id}{EndStyleBeginInlineString}" + + $"{cell.StringValue}" + + $"{EndStringCell}"); } public override bool TryWriteCellWithReference(string formulaText, in DataCell cachedValue, StyleId? styleId, DefaultStyling? defaultStyling, CellWriterState state) { - var buffer = state.Buffer; - var bytes = buffer.GetSpan(); - var written = 0; - - if (styleId is null) + if (styleId is { } style) { - if (!TryWriteCellStartWithReference(state, bytes, ref written)) return false; - if (!"\" t=\"str\">"u8.TryCopyTo(bytes, ref written)) return false; + return state.Buffer.TryWrite( + $"{state}{EndReferenceBeginFormulaCellStyle}{style.Id}{FormulaCellHelper.EndQuoteBeginFormula}" + + $"{formulaText}" + + $"{FormulaCellHelper.EndFormulaBeginCachedValue}" + + $"{cachedValue.StringValue}" + + $"{FormulaCellHelper.EndCachedValueEndCell}"); } - else - { - if (!TryWriteCellStartWithReference(state, bytes, ref written)) return false; - if (!"\" t=\"str\" s=\""u8.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(styleId.Id, bytes, ref written)) return false; - if (!FormulaCellHelper.EndStyleBeginFormula.TryCopyTo(bytes, ref written)) return false; - } - - if (!SpanHelper.TryWrite(formulaText, bytes, ref written)) return false; - if (!FormulaCellHelper.EndFormulaBeginCachedValue.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(cachedValue.StringValue, bytes, ref written)) return false; - if (!FormulaCellHelper.EndCachedValueEndCell.TryCopyTo(bytes, ref written)) return false; - buffer.Advance(written); - return true; + return state.Buffer.TryWrite( + $"{state}{EndReferenceBeginFormula}" + + $"{formulaText}" + + $"{FormulaCellHelper.EndFormulaBeginCachedValue}" + + $"{cachedValue.StringValue}" + + $"{FormulaCellHelper.EndCachedValueEndCell}"); } public override bool TryWriteEndElement(SpreadsheetBuffer buffer) { - var bytes = buffer.GetSpan(); - if (!EndStringCell.TryCopyTo(bytes)) - return false; - - buffer.Advance(EndStringCell.Length); - return true; + return buffer.TryWrite($"{EndStringCell}"); } public override bool TryWriteEndElement(in Cell cell, SpreadsheetBuffer buffer) { - if (cell.Formula is null) - return TryWriteEndElement(buffer); - - var bytes = buffer.GetSpan(); - if (!FormulaCellHelper.EndCachedValueEndCell.TryCopyTo(bytes)) - return false; - - buffer.Advance(FormulaCellHelper.EndCachedValueEndCell.Length); - return true; + return cell.Formula is null + ? TryWriteEndElement(buffer) + : buffer.TryWrite($"{FormulaCellHelper.EndCachedValueEndCell}"); } public override bool WriteFormulaStartElement(StyleId? styleId, DefaultStyling? defaultStyling, SpreadsheetBuffer buffer) { - var bytes = buffer.GetSpan(); - var written = 0; - - if (styleId is null) - { - if (!BeginStringFormulaCell.TryCopyTo(bytes, ref written)) return false; - buffer.Advance(written); - return true; - } - - if (!BeginStyledStringCell.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(styleId.Id, bytes, ref written)) return false; - if (!FormulaCellHelper.EndStyleBeginFormula.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; + return styleId is { } style + ? buffer.TryWrite($"{BeginStyledStringCell}{style.Id}{FormulaCellHelper.EndQuoteBeginFormula}") + : buffer.TryWrite($"{BeginStringFormulaCell}"); } public override bool WriteFormulaStartElementWithReference(StyleId? styleId, DefaultStyling? defaultStyling, CellWriterState state) { - var buffer = state.Buffer; - var bytes = buffer.GetSpan(); - var written = 0; - - if (styleId is null) - { - if (!TryWriteCellStartWithReference(state, bytes, ref written)) return false; - if (!"\" t=\"str\">"u8.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; - } - - if (!TryWriteCellStartWithReference(state, bytes, ref written)) return false; - if (!"\" t=\"inlineStr\" s=\""u8.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(styleId.Id, bytes, ref written)) return false; - if (!FormulaCellHelper.EndStyleBeginFormula.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; + return styleId is { } style + ? state.Buffer.TryWrite($"{state}{EndReferenceBeginStyle}{style.Id}{FormulaCellHelper.EndQuoteBeginFormula}") + : state.Buffer.TryWrite($"{state}{EndReferenceBeginFormula}"); } public override bool WriteStartElement(SpreadsheetBuffer buffer) { - var bytes = buffer.GetSpan(); - var written = 0; - - if (!BeginStringCell.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; + return buffer.TryWrite($"{BeginStringCell}"); } public override bool WriteStartElement(StyleId styleId, SpreadsheetBuffer buffer) { - var bytes = buffer.GetSpan(); - var written = 0; - - if (!BeginStyledStringCell.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(styleId.Id, bytes, ref written)) return false; - if (!EndStyleBeginInlineString.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; + return buffer.TryWrite($"{BeginStyledStringCell}{styleId.Id}{EndStyleBeginInlineString}"); } public override bool WriteStartElementWithReference(CellWriterState state) { - var buffer = state.Buffer; - var bytes = buffer.GetSpan(); - var written = 0; - - if (!TryWriteCellStartWithReference(state, bytes, ref written)) return false; - if (!"\" t=\"inlineStr\">"u8.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; + return state.Buffer.TryWrite($"{state}{EndReferenceBeginString}"); } public override bool WriteStartElementWithReference(StyleId styleId, CellWriterState state) { - var buffer = state.Buffer; - var bytes = buffer.GetSpan(); - var written = 0; - - if (!TryWriteCellStartWithReference(state, bytes, ref written)) return false; - if (!"\" t=\"inlineStr\" s=\""u8.TryCopyTo(bytes, ref written)) return false; - if (!SpanHelper.TryWrite(styleId.Id, bytes, ref written)) return false; - if (!EndStyleBeginInlineString.TryCopyTo(bytes, ref written)) return false; - - buffer.Advance(written); - return true; + return state.Buffer.TryWrite($"{state}{EndReferenceBeginStyle}{styleId.Id}{EndStyleBeginInlineString}"); } public override bool CanWriteValuePieceByPiece(in DataCell cell) => true; diff --git a/SpreadCheetah/CellValueWriters/Time/DateTimeCellValueWriter.cs b/SpreadCheetah/CellValueWriters/Time/DateTimeCellValueWriter.cs index 1df01cd8..f0f73d10 100644 --- a/SpreadCheetah/CellValueWriters/Time/DateTimeCellValueWriter.cs +++ b/SpreadCheetah/CellValueWriters/Time/DateTimeCellValueWriter.cs @@ -1,8 +1,8 @@ using SpreadCheetah.CellValueWriters.Number; using SpreadCheetah.CellWriters; +using SpreadCheetah.Helpers; using SpreadCheetah.Styling; using SpreadCheetah.Styling.Internal; -using System.Buffers.Text; namespace SpreadCheetah.CellValueWriters.Time; @@ -10,37 +10,90 @@ internal sealed class DateTimeCellValueWriter : NumberCellValueWriterBase { protected override int GetStyleId(StyleId styleId) => styleId.DateTimeId; - protected override bool TryWriteValue(in DataCell cell, Span destination, out int bytesWritten) + public override bool TryWriteCell(in DataCell cell, DefaultStyling? defaultStyling, SpreadsheetBuffer buffer) { - return Utf8Formatter.TryFormat(cell.NumberValue.DoubleValue, destination, out bytesWritten); + return defaultStyling?.DateTimeStyleId is { } styleId + ? TryWriteDateTimeCell(cell, styleId, buffer) + : buffer.TryWrite($"{BeginDataCell}{cell.NumberValue.DoubleValue}{EndDefaultCell}"); } - public override bool TryWriteCell(in DataCell cell, DefaultStyling? defaultStyling, SpreadsheetBuffer buffer) + public override bool TryWriteCell(in DataCell cell, StyleId styleId, SpreadsheetBuffer buffer) { - var defaultStyleId = defaultStyling?.DateTimeStyleId; - return defaultStyleId is not null - ? TryWriteCell(cell, defaultStyleId.Value, buffer) - : TryWriteCell(cell, buffer); + return TryWriteDateTimeCell(cell, styleId.DateTimeId, buffer); } public override bool TryWriteCell(string formulaText, in DataCell cachedValue, StyleId? styleId, DefaultStyling? defaultStyling, SpreadsheetBuffer buffer) { var actualStyleId = styleId?.DateTimeId ?? defaultStyling?.DateTimeStyleId; - return TryWriteCell(formulaText, cachedValue, actualStyleId, buffer); + if (actualStyleId is { } style) + { + return buffer.TryWrite( + $"{StyledCellHelper.BeginStyledNumberCell}{style}{FormulaCellHelper.EndQuoteBeginFormula}" + + $"{formulaText}" + + $"{FormulaCellHelper.EndFormulaBeginCachedValue}" + + $"{cachedValue.NumberValue.DoubleValue}" + + $"{FormulaCellHelper.EndCachedValueEndCell}"); + } + + return buffer.TryWrite( + $"{FormulaCellHelper.BeginNumberFormulaCell}" + + $"{formulaText}" + + $"{FormulaCellHelper.EndFormulaBeginCachedValue}" + + $"{cachedValue.NumberValue.DoubleValue}" + + $"{FormulaCellHelper.EndCachedValueEndCell}"); + } + + private static bool TryWriteDateTimeCell(in DataCell cell, int styleId, SpreadsheetBuffer buffer) + { + return buffer.TryWrite( + $"{StyledCellHelper.BeginStyledNumberCell}{styleId}{EndQuoteBeginValue}" + + $"{cell.NumberValue.DoubleValue}" + + $"{EndDefaultCell}"); } public override bool TryWriteCellWithReference(in DataCell cell, DefaultStyling? defaultStyling, CellWriterState state) { - var defaultStyleId = defaultStyling?.DateTimeStyleId; - return defaultStyleId is not null - ? TryWriteCellWithReference(cell, defaultStyleId.Value, state) - : TryWriteCellWithReference(cell, state); + if (defaultStyling?.DateTimeStyleId is { } styleId) + return TryWriteDateTimeCellWithReference(cell, styleId, state); + + return state.Buffer.TryWrite( + $"{state}{EndQuoteBeginValue}" + + $"{cell.NumberValue.DoubleValue}" + + $"{EndDefaultCell}"); + } + + public override bool TryWriteCellWithReference(in DataCell cell, StyleId styleId, CellWriterState state) + { + return TryWriteDateTimeCellWithReference(cell, styleId.DateTimeId, state); } public override bool TryWriteCellWithReference(string formulaText, in DataCell cachedValue, StyleId? styleId, DefaultStyling? defaultStyling, CellWriterState state) { var actualStyleId = styleId?.DateTimeId ?? defaultStyling?.DateTimeStyleId; - return TryWriteCellWithReference(formulaText, cachedValue, actualStyleId, state); + if (actualStyleId is { } style) + { + return state.Buffer.TryWrite( + $"{state}{StyledCellHelper.EndReferenceBeginStyleId}{style}{FormulaCellHelper.EndQuoteBeginFormula}" + + $"{formulaText}" + + $"{FormulaCellHelper.EndFormulaBeginCachedValue}" + + $"{cachedValue.NumberValue.DoubleValue}" + + $"{FormulaCellHelper.EndCachedValueEndCell}"); + } + + return state.Buffer.TryWrite( + $"{state}{FormulaCellHelper.EndQuoteBeginFormula}" + + $"{formulaText}" + + $"{FormulaCellHelper.EndFormulaBeginCachedValue}" + + $"{cachedValue.NumberValue.DoubleValue}" + + $"{FormulaCellHelper.EndCachedValueEndCell}"); + } + + private static bool TryWriteDateTimeCellWithReference(in DataCell cell, int styleId, CellWriterState state) + { + return state.Buffer.TryWrite( + $"{state}{StyledCellHelper.EndReferenceBeginStyleId}{styleId}{EndQuoteBeginValue}" + + $"{cell.NumberValue.DoubleValue}" + + $"{EndDefaultCell}"); } public override bool WriteFormulaStartElement(StyleId? styleId, DefaultStyling? defaultStyling, SpreadsheetBuffer buffer) @@ -54,4 +107,9 @@ public override bool WriteFormulaStartElementWithReference(StyleId? styleId, Def var actualStyleId = styleId?.DateTimeId ?? defaultStyling?.DateTimeStyleId; return WriteFormulaStartElementWithReference(actualStyleId, state); } + + public override bool WriteValuePieceByPiece(in DataCell cell, SpreadsheetBuffer buffer, ref int valueIndex) + { + return buffer.TryWrite($"{cell.NumberValue.DoubleValue}"); + } } diff --git a/SpreadCheetah/CellWriters/BaseCellWriter.cs b/SpreadCheetah/CellWriters/BaseCellWriter.cs index 4673e5a9..7d128e57 100644 --- a/SpreadCheetah/CellWriters/BaseCellWriter.cs +++ b/SpreadCheetah/CellWriters/BaseCellWriter.cs @@ -121,17 +121,8 @@ private bool TryAddRowCellsForList(IList cells) return TryWriteRowEnd(); } - private bool TryWriteRowEnd() - { - var rowEnd = ""u8; - if (rowEnd.TryCopyTo(Buffer.GetSpan())) - { - Buffer.Advance(rowEnd.Length); - return true; - } - - return false; - } + private static ReadOnlySpan RowEnd => ""u8; + private bool TryWriteRowEnd() => Buffer.TryWrite($"{RowEnd}"); private async ValueTask WriteRowEndAsync(Stream stream, CancellationToken token) { diff --git a/SpreadCheetah/Helpers/FormulaCellHelper.cs b/SpreadCheetah/Helpers/FormulaCellHelper.cs index 30f9a7ff..fbd7c6ea 100644 --- a/SpreadCheetah/Helpers/FormulaCellHelper.cs +++ b/SpreadCheetah/Helpers/FormulaCellHelper.cs @@ -2,7 +2,7 @@ namespace SpreadCheetah.Helpers; internal static class FormulaCellHelper { - public static ReadOnlySpan EndStyleBeginFormula => "\">"u8; + public static ReadOnlySpan EndQuoteBeginFormula => "\">"u8; public static ReadOnlySpan BeginNumberFormulaCell => ""u8; public static ReadOnlySpan EndFormulaBeginCachedValue => ""u8; public static ReadOnlySpan EndCachedValueEndCell => ""u8; diff --git a/SpreadCheetah/MetadataXml/RelationshipsXml.cs b/SpreadCheetah/MetadataXml/RelationshipsXml.cs index de3b787b..5aa00b20 100644 --- a/SpreadCheetah/MetadataXml/RelationshipsXml.cs +++ b/SpreadCheetah/MetadataXml/RelationshipsXml.cs @@ -24,9 +24,8 @@ public static async ValueTask WriteAsync( await using (stream.ConfigureAwait(false)) #endif { - Debug.Assert(Content.Length <= buffer.FreeCapacity); - Content.TryCopyTo(buffer.GetSpan()); - buffer.Advance(Content.Length); + var ok = buffer.TryWrite($"{Content}"); + Debug.Assert(ok); await buffer.FlushToStreamAsync(stream, token).ConfigureAwait(false); } } diff --git a/SpreadCheetah/SpreadsheetBuffer.cs b/SpreadCheetah/SpreadsheetBuffer.cs index 9096c183..b7ef0375 100644 --- a/SpreadCheetah/SpreadsheetBuffer.cs +++ b/SpreadCheetah/SpreadsheetBuffer.cs @@ -1,17 +1,18 @@ +using SpreadCheetah.CellWriters; using SpreadCheetah.Helpers; +using System.Buffers.Text; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Runtime.CompilerServices; namespace SpreadCheetah; -internal sealed class SpreadsheetBuffer +internal sealed class SpreadsheetBuffer(byte[] buffer) { - private readonly byte[] _buffer; + private readonly byte[] _buffer = buffer; private int _index; - public SpreadsheetBuffer(byte[] buffer) - { - _buffer = buffer; - } - public Span GetSpan() => _buffer.AsSpan(_index); public int FreeCapacity => _buffer.Length - _index; public void Advance(int bytes) => _index += bytes; @@ -38,4 +39,132 @@ public ValueTask FlushToStreamAsync(Stream stream, CancellationToken token) return stream.WriteAsync(_buffer.AsMemory(0, index), token); #endif } + + public bool TryWrite([InterpolatedStringHandlerArgument("")] ref TryWriteInterpolatedStringHandler handler) + { + if (handler._success) + { + Advance(handler._pos); + return true; + } + + return false; + } + + [InterpolatedStringHandler] + public ref struct TryWriteInterpolatedStringHandler + { + private readonly SpreadsheetBuffer _buffer; + internal int _pos; + internal bool _success; + + public TryWriteInterpolatedStringHandler(int literalLength, int formattedCount, SpreadsheetBuffer buffer) + { + _ = literalLength; + _ = formattedCount; + _buffer = buffer; + _success = true; + } + + private readonly Span GetSpan() => _buffer._buffer.AsSpan(_buffer._index + _pos); + + [ExcludeFromCodeCoverage] + public bool AppendLiteral(string value) + { + Debug.Fail("Use ReadOnlySpan instead of string literals"); + + if (value is not null && Utf8Helper.TryGetBytes(value, GetSpan(), out var bytesWritten)) + { + _pos += bytesWritten; + return true; + } + + return Fail(); + } + + public bool AppendFormatted(int value) + { + if (Utf8Formatter.TryFormat(value, GetSpan(), out var bytesWritten)) + { + _pos += bytesWritten; + return true; + } + + return Fail(); + } + + public bool AppendFormatted(float value) + { + if (Utf8Formatter.TryFormat(value, GetSpan(), out var bytesWritten)) + { + _pos += bytesWritten; + return true; + } + + return Fail(); + } + + public bool AppendFormatted(double value) + { + if (Utf8Formatter.TryFormat(value, GetSpan(), out var bytesWritten)) + { + _pos += bytesWritten; + return true; + } + + return Fail(); + } + + [ExcludeFromCodeCoverage] + public bool AppendFormatted(T value) + { + Debug.Fail("Create non-generic overloads to avoid allocations when running on .NET Framework"); + + string? s = value is IFormattable f + ? f.ToString(null, CultureInfo.InvariantCulture) + : value?.ToString(); + + return AppendFormatted(s); + } + + public bool AppendFormatted(string? value) + { + if (Utf8Helper.TryGetBytes(value, GetSpan(), out int bytesWritten)) + { + _pos += bytesWritten; + return true; + } + + return Fail(); + } + + public bool AppendFormatted(scoped ReadOnlySpan utf8Value) + { + if (utf8Value.TryCopyTo(GetSpan())) + { + _pos += utf8Value.Length; + return true; + } + + return Fail(); + } + + public bool AppendFormatted(CellWriterState state) + { + var bytes = GetSpan(); + var bytesWritten = 0; + + if (!"