// 根据 OID 和字符内容,构造一个 element 的原始数据 // parameters: // text 内容文字。如果是给 ISIL 类型的, 要明确用 compact_method 指明 // alignment 对齐 block 边界 public static byte[] Compact(int oid, string text, CompactionScheme compact_method, bool alignment) { Precursor precursor = new Precursor(); precursor.ObjectIdentifier = oid; // 自动选定压缩方案 if (compact_method == CompactionScheme.Null) { compact_method = Compress.AutoSelectCompressMethod(text); if (compact_method == CompactionScheme.Null) { throw new Exception($"无法为字符串 '{text}' 自动选定压缩方案"); } } if (compact_method == CompactionScheme.ISIL) { precursor.CompactionCode = (int)CompactionScheme.ApplicationDefined; } else { precursor.CompactionCode = (int)compact_method; } byte[] data = null; if (compact_method == CompactionScheme.Integer) { data = Compress.IntegerCompress(text); } else if (compact_method == CompactionScheme.Numeric) { data = Compress.NumericCompress(text); } else if (compact_method == CompactionScheme.FivebitCode) { data = Compress.Bit5Compress(text); } else if (compact_method == CompactionScheme.SixBitCode) { data = Compress.Bit6Compress(text); } else if (compact_method == CompactionScheme.Integer) { data = Compress.IntegerCompress(text); } else if (compact_method == CompactionScheme.SevenBitCode) { data = Compress.Bit7Compress(text); } else if (compact_method == CompactionScheme.OctectString) { data = Encoding.ASCII.GetBytes(text); } else if (compact_method == CompactionScheme.Utf8String) { data = Encoding.UTF8.GetBytes(text); } else if (compact_method == CompactionScheme.ISIL) { data = Compress.IsilCompress(text); } else if (compact_method == CompactionScheme.Base64) { data = Convert.FromBase64String(text); } // 通过 data 计算出是否需要 padding bytes int total_bytes = 1 // precursor + 1 // length of data + data.Length; // data if (precursor.ObjectIdentifier >= 15) { total_bytes++; // relative-OID 需要多占一个 byte } int paddings = 0; if ((total_bytes % 4) != 0) { paddings = 4 - (total_bytes % 4); } // 组装最终数据 List <byte> result = new List <byte>(); // OID 值是否越过 precursor 表达范围 if (precursor.ObjectIdentifier >= 15) { precursor.ObjectIdentifier = 0x0f; } if (paddings > 0) { precursor.Offset = true; } result.Add(precursor.ToByte()); if (precursor.ObjectIdentifier == 0x0f) { Debug.Assert(oid >= 15); result.Add((byte)(oid - 15)); // relative-OID } // padding length byte if (paddings > 0) { result.Add((byte)(paddings - 1)); } result.Add((byte)data.Length); // length of data result.AddRange(data); // data for (int i = 0; i < paddings - 1; i++) { result.Add((byte)0); // padding bytes } return(result.ToArray()); }
// 对元素进行排序 // 调用前,要确保每个元素都 Compact 好了,内容放入 OriginData 中了 // 排序原则: // 1) PII 在第一个; // 2) Content Parameter 在第二个; // 2.1) 如果元素里面至少有一个锁定元素,Content Parameter 元素要对齐 block 边界(便于锁定元素锁定) // 3) 其余拟锁定元素聚集在一起,最后给必要的 padding // 4) 所有非锁定的元素聚集在一起 // 5) 锁定的元素,和非锁定元素区域内部,可以按照 OID 号码排序 // 上述算法有个优势,就是所有锁定元素中间不一定要 block 边界对齐,这样可以节省一点空间 // 但存在一个小问题: Content Parameter 要占用多少 byte? 如果以后元素数量增多,(因为它后面就是锁定区域)它无法变大怎么办? public void Sort(int max_bytes, int block_size, bool trim_cp_right) { SetContentParameter(trim_cp_right); if (this.IsNew == true) { this._elements.Sort((a, b) => { // OID 为 1 的始终靠前 if ((int)a.OID == 1) { if (a.OID == b.OID) return 0; return -1; } if ((int)b.OID == 1) return 1; // 比较 WillLock。拟锁定的位置靠后 int lock_a = a.WillLock ? 1 : 0; int lock_b = b.WillLock ? 1 : 0; int delta = lock_a - lock_b; if (delta != 0) return delta; // 最后比较 OID。OID 值小的靠前 delta = a.OID - b.OID; return delta; }); // 更新 element.OriginData // 对 WillLock 切换的情形,和最后一个 WillLock 元素进行 padding 调整 Element prev_element = null; string prev_type = ""; // free or will_lock string current_type = ""; int start = 0; int i = 0; foreach (Element element in this._elements) { if (element.WillLock == false) current_type = "free"; else current_type = "will_lock"; // 如果切换了类型(类型指“普通”还是 WillLock) // 需要把 prev_element 尾部对齐 block 边界 if (current_type != prev_type && prev_element != null) { int result = AdjustCount(block_size, start); int delta = result - start; // 调整 prev_element 的 padding if (delta != 0) prev_element.OriginData = Element.AdjustPaddingBytes(prev_element.OriginData, delta); start = result; } #if NO CompactionScheme compact_method = CompactionScheme.Null; if (element.OID == ElementOID.ContentParameter) compact_method = CompactionScheme.OctectString; else if (element.OID == ElementOID.OwnerInstitution || element.OID == ElementOID.IllBorrowingInstitution) compact_method = CompactionScheme.ISIL; #endif element.OriginData = Element.Compact((int)element.OID, element.Text, CompactionScheme.Null, false); //if (start != element.StartOffs) // throw new Exception($"element {element.ToString()} 的 StartOffs {element.StartOffs} 不符合预期值 {start}"); start += element.OriginData.Length; prev_type = current_type; prev_element = element; i++; } // 如果是最后一个 element(并且它是 WillLock 类型) // 需要把尾部对齐 block 边界 if (prev_element != null && prev_element.WillLock) { int result = AdjustCount(block_size, start); int delta = result - start; // 调整 prev_element 的 padding if (delta != 0) prev_element.OriginData = Element.AdjustPaddingBytes(prev_element.OriginData, delta); start = result; } } else { foreach (Element element in this._elements) { if (element.Locked) continue; element.OriginData = Element.Compact((int)element.OID, element.Text, CompactionScheme.Null, false); } // 改写状态。Locked 的元素不能动。只能在余下空挡位置,见缝插针组合排列非 Lock 元素 // 组合算法是:每当得到一个区间尺寸,就去查询余下的元素中尺寸组合起来最适合的那些元素 // 可以用一种利用率指标来评价组合好坏。也就是余下的空挡越小,越好 List<int> elements = GetFreeElements(); if (elements.Count == 0) return; // 没有必要进行排序 bool bRet = GetFreeSegments(max_bytes, out List<int> free_segments, out List<object> anchor_list); if (bRet == false) throw new Exception("当前没有任何自由空间可用于存储自由字段信息"); // 对 n 个区间进行填充尝试。把每个可能的组合,都填充试验一轮 // parameters: // areas 空闲区间的列表。每个数字表示空闲的 byte 数 // elements 元素尺寸列表。每个数字表示元素占用的 byte 数 List<List<OneArea>> layouts = GetPossibleLayouts( block_size, free_segments, elements); if (layouts.Count == 0) throw new Exception("没有找到可用的排列方式"); // TODO: 如何优选 layouts? 目前能想到的,是把 ContentParameter 在第一个元素以后的优选出来 // 安排元素顺序 SetElementsPos( block_size, layouts[0], free_segments, anchor_list); } }
// 根据 OID 和字符内容,构造一个 element 的原始数据 // parameters: // text 内容文字。如果是给 ISIL 类型的, 要明确用 compact_method 指明 // alignment 对齐 block 边界 public static byte[] Compact(int oid, string text, CompactionScheme compact_method, bool alignment) { Precursor precursor = new Precursor(); precursor.ObjectIdentifier = oid; // 注: SetInformation 的编码方式是根据字符串来自动选定的 (GB/T 35660.2-2017 page 32 例子) if (oid == (int)ElementOID.ContentParameter || oid == (int)ElementOID.TypeOfUsage || oid == (int)ElementOID.MediaFormat || oid == (int)ElementOID.SupplyChainStage) { compact_method = CompactionScheme.OctectString; } else if (oid == (int)ElementOID.OwnerInstitution || oid == (int)ElementOID.IllBorrowingInstitution) { compact_method = CompactionScheme.ISIL; } // 自动选定压缩方案 if (compact_method == CompactionScheme.Null) { compact_method = RFID.Compact.AutoSelectCompactMethod((ElementOID)oid, text); if (compact_method == CompactionScheme.Null) { throw new Exception($"无法为字符串 '{text}' (oid='{(ElementOID)oid}') 自动选定压缩方案"); } } byte[] data = null; if (compact_method == CompactionScheme.Integer) { data = RFID.Compact.IntegerCompact(text); } else if (compact_method == CompactionScheme.Numeric) { data = RFID.Compact.NumericCompact(text); } else if (compact_method == CompactionScheme.FivebitCode) { data = RFID.Compact.Bit5Compact(text); } else if (compact_method == CompactionScheme.SixBitCode) { data = RFID.Compact.Bit6Compact(text); } else if (compact_method == CompactionScheme.Integer) { data = RFID.Compact.IntegerCompact(text); } else if (compact_method == CompactionScheme.SevenBitCode) { data = RFID.Compact.Bit7Compact(text); } else if (compact_method == CompactionScheme.OctectString) { data = FromHexString(text); } else if (compact_method == CompactionScheme.Utf8String) { data = Encoding.UTF8.GetBytes(text); } else if (compact_method == CompactionScheme.ISIL) { data = RFID.Compact.IsilCompact(text); } else if (compact_method == CompactionScheme.Base64) { data = Convert.FromBase64String(text); } if (oid == (int)ElementOID.ContentParameter) { compact_method = 0; } if (compact_method == CompactionScheme.ISIL) { compact_method = (int)CompactionScheme.ApplicationDefined; } precursor.CompactionCode = (int)compact_method; // 通过 data 计算出是否需要 padding bytes int total_bytes = 1 // precursor + 1 // length of data + data.Length; // data if (precursor.ObjectIdentifier >= 15) { total_bytes++; // relative-OID 需要多占一个 byte } int paddings = 0; if (alignment) { if ((total_bytes % 4) != 0) { paddings = 4 - (total_bytes % 4); } } // 组装最终数据 List <byte> result = new List <byte>(); // OID 值是否越过 precursor 表达范围 if (precursor.ObjectIdentifier >= 15) { precursor.ObjectIdentifier = 0x0f; } if (paddings > 0) { precursor.Offset = true; } result.Add(precursor.ToByte()); if (precursor.ObjectIdentifier == 0x0f) { Debug.Assert(oid >= 15); result.Add((byte)(oid - 15)); // relative-OID } // padding length byte if (paddings > 0) { result.Add((byte)(paddings - 1)); } result.Add((byte)data.Length); // length of data result.AddRange(data); // data for (int i = 0; i < paddings - 1; i++) { result.Add((byte)0); // padding bytes } return(result.ToArray()); }