Skip to content

Commit ccab889

Browse files
committed
Centralize data segment info dispatching
1 parent 644301d commit ccab889

13 files changed

Lines changed: 299 additions & 146 deletions

CONTEXT.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,15 @@ The 8-bit symbols the payload becomes after a version and error correction level
5151
chosen: data codewords (segment bits + terminator + padding) followed by Reed-Solomon
5252
error correction codewords, interleaved per spec, ready to fill into the matrix.
5353

54+
**Data segment mode info**:
55+
The per-mode rules of a `DataSegmentMode` — mode indicator, count-indicator widths, and
56+
(for the data modes: numeric, alphanumeric, Kanji, binary) the bit-length/byte-count
57+
formulas and the segment factory — held in one internal descriptor per mode and looked
58+
up by mode value. The public `DataSegmentMode` enum stays a plain enum; the behavior
59+
keyed off it lives in the descriptor.
60+
_Avoid_: putting per-mode logic on the public enum, or re-deriving it via `switch`/
61+
`(int)mode` arithmetic at each call site.
62+
5463
## Relationships
5564

5665
- The encode pipeline is: text → data segments → **codewords****module** matrix.

QrCodeGenerator/ArraySegmentExtensions.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,23 @@ internal static class ArraySegmentExtensions
2323
/// needed as the struct includes an indexer.</remarks>
2424
internal static T At<T>(this ArraySegment<T> segment, int index)
2525
{
26-
Debug.Assert(segment.Array != null);
26+
Trace.Assert(segment.Array != null);
2727
return segment.Array[segment.Offset + index];
2828
}
2929

3030
/// <summary>
3131
/// Forms a slice of the specified length out of the current array segment starting at the specified index.
3232
/// </summary>
3333
/// <param name="segment">The array segment.</param>
34-
/// <param name="startIndex">The index at which the slices starts.</param>
34+
/// <param name="startIndex">The index at which the slice starts.</param>
3535
/// <param name="length">The length of the slice.</param>
3636
/// <typeparam name="T">The type of the elements in the array segment.</typeparam>
3737
/// <returns>An array segment.</returns>
3838
/// <remarks>In later versions of the .NET Framework, this method is not
3939
/// needed as it was added to the struct.</remarks>
4040
internal static ArraySegment<T> MakeSlice<T>(this ArraySegment<T> segment, int startIndex, int length)
4141
{
42-
Debug.Assert(segment.Array != null);
42+
Trace.Assert(segment.Array != null);
4343
return new ArraySegment<T>(segment.Array, segment.Offset + startIndex, length);
4444
}
4545
}

QrCodeGenerator/DataSegment.cs

Lines changed: 21 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public abstract class DataSegment
7575
/// The position of the QR code within a structured append message.
7676
/// <para>
7777
/// Valid positions are between 1 and 16.
78-
/// The value is only valuid if this segment uses the segment mode <see cref="DataSegmentMode.StructuredAppend"/>.
78+
/// The value is only valid if this segment uses the segment mode <see cref="DataSegmentMode.StructuredAppend"/>.
7979
/// </para>
8080
/// </summary>
8181
public virtual int StructuredAppendPosition => 0;
@@ -84,15 +84,15 @@ public abstract class DataSegment
8484
/// The total number of QR codes used for the structured append message.
8585
/// <para>
8686
/// Valid numbers are between 1 and 16.
87-
/// The value is only valuid if this segment uses the segment mode <see cref="DataSegmentMode.StructuredAppend"/>.
87+
/// The value is only valid if this segment uses the segment mode <see cref="DataSegmentMode.StructuredAppend"/>.
8888
/// </para>
8989
/// </summary>
9090
public virtual int StructuredAppendTotal => 0;
9191

9292
/// <summary>
9393
/// The data parity in the structured append messages.
9494
/// <para>
95-
/// The value is only valuid if this segment uses the segment mode <see cref="DataSegmentMode.StructuredAppend"/>.
95+
/// The value is only valid if this segment uses the segment mode <see cref="DataSegmentMode.StructuredAppend"/>.
9696
/// </para>
9797
/// </summary>
9898
public virtual byte StructuredAppendParity => 0;
@@ -274,19 +274,20 @@ internal static BitStream CreateBitStream(List<DataSegment> segments, int versio
274274
var bitStream = new BitStream(capacity);
275275
foreach (var segment in segments)
276276
{
277+
var modeInfo = DataSegmentModeInfo.For(segment.Mode);
277278
// mode indicator
278-
bitStream.AppendBits(GetModeIndicator(segment.Mode), 4);
279+
bitStream.AppendBits((uint)modeInfo.ModeIndicator, 4);
279280
// character count indicator
280-
if (segment.Mode >= DataSegmentMode.Numeric && segment.Mode <= DataSegmentMode.Binary)
281+
if (modeInfo.HasCountIndicator)
281282
{
282283
bitStream.AppendBits((uint)segment.DataBytes.Count,
283-
GetCountIndicatorLength(segment.Mode, version));
284+
modeInfo.GetCountIndicatorLength(version));
284285
}
285286
// data bit stream
286287
segment.WriteToBitStream(bitStream);
287288
}
288289

289-
Debug.Assert(bitStream.Length <= 8 * capacity);
290+
Trace.Assert(bitStream.Length <= 8 * capacity);
290291

291292
// terminator
292293
var terminatorLength = Math.Min(4, capacity * 8 - bitStream.Length);
@@ -309,17 +310,7 @@ internal static BitStream CreateBitStream(List<DataSegment> segments, int versio
309310

310311
internal static uint GetModeIndicator(DataSegmentMode mode)
311312
{
312-
uint symbol = 0;
313-
switch (mode)
314-
{
315-
case DataSegmentMode.Numeric: symbol = 1; break;
316-
case DataSegmentMode.Alphanumeric: symbol = 2; break;
317-
case DataSegmentMode.Kanji: symbol = 8; break;
318-
case DataSegmentMode.Binary: symbol = 4; break;
319-
case DataSegmentMode.ECI: symbol = 7; break;
320-
case DataSegmentMode.StructuredAppend: symbol = 3; break;
321-
}
322-
return symbol;
313+
return (uint)DataSegmentModeInfo.For(mode).ModeIndicator;
323314
}
324315

325316
#endregion
@@ -353,20 +344,10 @@ protected DataSegment(DataSegmentMode mode, int encodedLength)
353344
public static DataSegment MakeSegment(DataSegmentMode dataSegmentMode, ArraySegment<byte> dataBytes)
354345
{
355346
Objects.RequireNonNull(dataBytes.Array, "dataBytes.Array");
356-
357-
switch (dataSegmentMode)
358-
{
359-
case DataSegmentMode.Numeric:
360-
return new DataSegmentNumeric(dataBytes);
361-
case DataSegmentMode.Alphanumeric:
362-
return new DataSegmentAlphanumeric(dataBytes);
363-
case DataSegmentMode.Kanji:
364-
return new DataSegmentKanji(dataBytes);
365-
case DataSegmentMode.Binary:
366-
return new DataSegmentByte(dataBytes);
367-
default:
368-
throw new ArgumentOutOfRangeException(nameof(dataSegmentMode), dataSegmentMode, "This mode is not supported by this function.");
369-
}
347+
348+
var create = DataSegmentModeInfo.For(dataSegmentMode).Create;
349+
Trace.Assert(create != null);
350+
return create(dataBytes);
370351
}
371352

372353
/// <summary>
@@ -397,35 +378,6 @@ public static DataSegment MakeStructuredAppend(int position, int total, byte par
397378

398379
#region Length Calculation
399380

400-
/// <summary>
401-
/// Calculates the encoded length of the payload.
402-
/// <para>
403-
/// The length does not include the mode indicator and the count indicator.
404-
/// </para>
405-
/// <para>
406-
/// This function only supports the encoding modes <em>Numeric</em>,
407-
/// <em>AlphaNumeric</em>, <em>Kanji</em> and <em>Binary</em>.
408-
/// </para>
409-
/// </summary>
410-
/// <param name="mode">The encoding mode.</param>
411-
/// <param name="length">The number of bytes to encode.</param>
412-
/// <returns>The payload length, in bits.</returns>
413-
private static int GetDataBitLength(DataSegmentMode mode, int length)
414-
{
415-
switch (mode)
416-
{
417-
case DataSegmentMode.Numeric:
418-
return DataSegmentNumeric.GetNumericBitLength(length);
419-
case DataSegmentMode.Alphanumeric:
420-
return DataSegmentAlphanumeric.GetAlphanumericBitLength(length);
421-
case DataSegmentMode.Kanji:
422-
return DataSegmentKanji.GetKanjiBitLength(length);
423-
case DataSegmentMode.Binary:
424-
return DataSegmentByte.GetByteBitLength(length);
425-
default: throw new ArgumentOutOfRangeException(nameof(mode), mode, "This mode is not supported by this function.");
426-
}
427-
}
428-
429381
/// <summary>
430382
/// Calculates the segment length.
431383
/// <para>
@@ -442,7 +394,9 @@ private static int GetDataBitLength(DataSegmentMode mode, int length)
442394
/// <returns>The segment length, in bits.</returns>
443395
internal static int GetBitLength(DataSegmentMode mode, int length, int version)
444396
{
445-
return GetHeaderLength(mode, version) + GetDataBitLength(mode, length);
397+
var info = DataSegmentModeInfo.For(mode);
398+
Trace.Assert(info.EncodedBitLength != null);
399+
return info.GetHeaderLength(version) + info.EncodedBitLength(length);
446400
}
447401

448402
internal static int GetBitLength(IEnumerable<DataSegment> segments, int version)
@@ -462,18 +416,9 @@ internal static int GetBitLength(IEnumerable<DataSegment> segments, int version)
462416
/// <exception cref="ArgumentOutOfRangeException">Thrown if an unsupported mode is specified.</exception>
463417
internal static int GetByteCount(DataSegmentMode mode, int bitLength)
464418
{
465-
switch (mode)
466-
{
467-
case DataSegmentMode.Numeric:
468-
return DataSegmentNumeric.GetNumericByteCount(bitLength);
469-
case DataSegmentMode.Alphanumeric:
470-
return DataSegmentAlphanumeric.GetAlphanumericByteCount(bitLength);
471-
case DataSegmentMode.Kanji:
472-
return DataSegmentKanji.GetKanjiByteCount(bitLength);
473-
case DataSegmentMode.Binary:
474-
return DataSegmentByte.GetByteByteCount(bitLength);
475-
default: throw new ArgumentOutOfRangeException(nameof(mode), mode, "This mode is not supported by this function.");
476-
}
419+
var info = DataSegmentModeInfo.For(mode);
420+
Trace.Assert(info.ByteCount != null);
421+
return info.ByteCount(bitLength);
477422
}
478423

479424
/// <summary>
@@ -484,25 +429,8 @@ internal static int GetByteCount(DataSegmentMode mode, int bitLength)
484429
/// <returns>The header length, in bits.</returns>
485430
internal static int GetHeaderLength(DataSegmentMode mode, int version)
486431
{
487-
return 4 + GetCountIndicatorLength(mode, version);
432+
return DataSegmentModeInfo.For(mode).GetHeaderLength(version);
488433
}
489-
490-
private static int GetCountIndicatorLength(DataSegmentMode mode, int version)
491-
{
492-
// Groups are: 1 to 9, 10 to 26, 27 to 40
493-
var versionGroup = (version + 7) / 17;
494-
return CountIndicatorLength[(int)mode * 3 - 3 + versionGroup];
495-
}
496-
497-
private static readonly int[] CountIndicatorLength =
498-
{
499-
10, 12, 14, // numeric
500-
9, 11, 13, // alphanumeric
501-
8, 10, 12, // Kanji
502-
8, 16, 16, // binary
503-
0, 0, 0, // ECI designator
504-
0, 0, 0 // structured append
505-
};
506434

507435
#endregion
508436

@@ -531,7 +459,7 @@ public static string GetText(IEnumerable<DataSegment> segments)
531459
else
532460
{
533461
var dataBytes = segment.DataBytes;
534-
Debug.Assert(dataBytes.Array != null);
462+
Trace.Assert(dataBytes.Array != null);
535463
text.Append(encoding.GetString(dataBytes.Array, dataBytes.Offset, dataBytes.Count));
536464
}
537465
}

QrCodeGenerator/DataSegmentAlphanumeric.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ internal class DataSegmentAlphanumeric : DataSegment
1919
internal DataSegmentAlphanumeric(ArraySegment<byte> bytes)
2020
: base(DataSegmentMode.Alphanumeric, GetAlphanumericBitLength(bytes.Count))
2121
{
22-
Debug.Assert(bytes.Array != null);
22+
Trace.Assert(bytes.Array != null);
2323
DataBytes = bytes;
2424
}
2525

@@ -51,7 +51,7 @@ internal static int GetAlphanumericByteCount(int bitLength)
5151

5252
internal override void WriteToBitStream(BitStream bitStream)
5353
{
54-
Debug.Assert(DataBytes.Array != null);
54+
Trace.Assert(DataBytes.Array != null);
5555
for (var i = 0; i + 1 < DataBytes.Count; i += 2)
5656
{
5757
// 2 letters are encoded into 11 bits

QrCodeGenerator/DataSegmentByte.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ internal class DataSegmentByte : DataSegment
1818
internal DataSegmentByte(ArraySegment<byte> bytes)
1919
: base(DataSegmentMode.Binary, GetByteBitLength(bytes.Count))
2020
{
21-
Debug.Assert(bytes.Array != null);
21+
Trace.Assert(bytes.Array != null);
2222
DataBytes = bytes;
2323
}
2424

QrCodeGenerator/DataSegmentKanji.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ internal class DataSegmentKanji : DataSegment
1818
internal DataSegmentKanji(ArraySegment<byte> bytes)
1919
: base(DataSegmentMode.Kanji, GetKanjiBitLength(bytes.Count))
2020
{
21-
Debug.Assert(bytes.Array != null);
22-
Debug.Assert(bytes.Count % 2 == 0);
21+
Trace.Assert(bytes.Array != null);
22+
Trace.Assert(bytes.Count % 2 == 0);
2323
DataBytes = bytes;
2424
}
2525

@@ -56,7 +56,7 @@ internal static int GetKanjiByteCount(int bitLength)
5656
internal override void WriteToBitStream(BitStream bitStream)
5757
{
5858
var bytes = DataBytes;
59-
Debug.Assert(bytes.Array != null);
59+
Trace.Assert(bytes.Array != null);
6060

6161
for (var i = 0; i < bytes.Count; i += 2)
6262
{

0 commit comments

Comments
 (0)