好久沒更新博客了,我開源了一款高性能的二進制序列化器Bssom.Net和新穎的二進制協議Bssom,歡迎大家Star,歡迎參與專案貢獻!
Net開源技術交流群 976304396,禁止水,只能討論技術, 歡迎與我討論和性能相關的技識訓題!
另外,我還在抖音申請了一個賬號,用來記錄自己的日常生活, 想了解我平常是怎么寫代碼的嗎? 來關注我一下,哈哈! 抖音號: 198152455
Bssom.Net專案地址: https://github.com/1996v/Bssom.Net
Bssom協議地址: https://github.com/1996v/Bssom
A small, high performance, powerful serializer using bssom binary protocol
Bssom.Net是一個使用bssom結構協議實作的高性能結構化二進制序列化器,它具有以下特點,小巧,快速,功能性強.
- 小巧,檔案僅300多k
- 快速,它具有一流的序列化和反序列化性能
- 功能性強:
- 可以獲取物件被序列化后的大小而不用完整序列化物件
- 可以讀取物件中的某個元素而不用完整的反序列化物件
- 可以更改物件中的某個元素而不用完整的序列化
- 序列化后的格式具有自描述性
為什么需要?
目前c#已經有很多二進制序列化器, 但這些序列化器都只提供了單一的序列化和反序列化功能.
Bssom.Net采取了Bssom協議, 使序列化后的資料具有結構化特性, 且擁有直接對欄位進行編組的功能, 這使得Bssom.Net能做到其它序列化器所達不到的事情.
- 當我想在序列化物件時知道物件被序列化后的大小, 以提前來選擇該物件應該被序列化的正確位置(如資料庫引擎的FSM演算法), 那么Bssom.Net能夠滿足你
- 當我擁有一個大的二進制資料, 但是我只想無合約的讀取其中一個欄位, 以避免完整的反序列化開銷, 那么Bssom.Net能夠滿足你
- 當我擁有一個已經被序列化后的資料包, 我只想無合約的修改其中一個欄位, 以避免重新序列化的開銷, 那么Bssom.Net能夠滿足你
- 當我想讓物件被序列化后仍能保留型別資訊, 而不用依賴物體, 那么Bssom.Net能夠滿足你
什么是Bssom協議?
Bssom(Binary search algorithm structure model object binary marshalling)是一個使用二分查找演算法模型對物件進行結構化編組的協議,被編組后的資料具有特殊的元資料資訊,根據這些元資料資訊可以高效的僅讀取和更改物件中的某個元素,這樣可以在對大物件進行序列化和反序列化的程序中不必因為只讀取或只寫入一個欄位而造成完整的序列化開銷,
大綱
- 1.性能
- 2.讀寫器
- IBssomBuffer
- IBssomBufferWriter
- 3.格式化器
- 4.決議器
- PrimitiveResolver
- AttributeFormatterResolver
- BuildInResolver
- BssomValueResolver
- IDictionaryResolver
- ICollectionResolver
- MapCodeGenResolver
- ObjectResolver
- CompositedResolver
- 5.擴展
- 6.高級API
- BssomSerializer
- BssomSerializerOptions
- BssomSerializeContext
- 7.欄位編組
- BssomFieldMarshaller
- 簡單欄位訪問語言
- 自定義欄位訪問形式介面
- 8.動態代碼生成
- 9.特性
- 10.更多的可能性
- 11.如何使用
- Size
- Serialize
- Deserialize
- ReadValue
- ReadAllMapKeys
- TryWriteValue
- 如何使用特性
- 如何定義擴展
- 12.如何參與專案貢獻
- 13.誰在使用
- 14.其它
1.性能

這里是與.NET平臺下非常優秀的兩款序列化程式(MessagePack 和 Protobuf-net)進行性能比較的基準.
柱狀資料代表執行相同任務所花費的時間, 越低代表性能越快, 折線資料代表執行相同任務所產生的GC, 越低代表在執行中所產生的垃圾越少 , 從性能比較結果可以看出Bssom.Net的性能是非常優異的.
Bssom.Net使用很多技術來提高性能.
- 使用記憶體池技術, 用于寫入的記憶體可以復用
- 使用運算式和Emit動態編程技術, 對型別進行了特殊處理, 且避免值型別裝箱拆箱
- 使用泛型靜態快取, 避免了字典查找開銷
- 包裝了例外拋出代碼, 以增加行內的可能性
- 更多的對強型別進行呼叫, 而不是介面抽象
- 預處理Map2型別的元資料, 在序列化時不需要對其進行再次編碼
- 在查找Map2鍵時, 提前固定區域參考, 而不是標準函式呼叫
- 決議Map1型別時, 自動構建8位元組的自動機跳躍查找
- 值得一提的是, 出于減少依賴, 減少體積的目的, Bssom.Net并沒有依賴
System.Memory.dll, 因此無法使用Span<T>,Memory<T>等型別, 這意味著Bssom.Net的實作將無法使用ByReference<T>這一JIT內部特性, 因此目前的讀寫器將不具備讀寫區域化和去虛擬化及行內呼叫的這三個性能優化點 ( 但即使這樣, 目前的Bssom.Net性能依然非常優秀 ) , 若將來有可能支持Span<T>型別的話, 那么Bssom.Net將會通過一些額外的性能技巧來再次提升性能.
2.讀寫器
Bssom.Net對于讀取和寫入的入口并不是直接使用原生的Byte[], 而是提供了緩沖區介面IBssomBuffer和寫入器介面IBssomBufferWriter.
與原生的byte[]不同, 介面將更加靈活, 實作IBssomBuffer后可以從任意來源來讀取資料, 實作IBssomBufferWriter后可以將資料寫在任意地方(比如非連續的片段)
IBssomBuffer
IBssomBuffer是一個用于序列化的緩沖區介面, 提供了讀取的行為.
| 方法 | 描述 |
|---|---|
| Position | 緩沖區中的當前位置 |
| ReadRef | 從當前緩沖區中的位置讀取指定大小序列的參考 |
| Seek | 設定當前緩沖區的位置 |
| SeekWithOutVerify | 設定當前緩沖區的位置, 并且不對position的邊界進行驗證 |
| TryReadFixedRef | 嘗試從當前緩沖區中的位置讀取一個可以固定的位元組序列的參考, 當進行Seek操作的時候不會影響被固定位元組的參考位置 |
| UnFixed | 用于取消由TryReadFixedRef所固定的參考, 此方法的呼叫始終和TryReadFixedRef對稱 |
IBssomBufferWriter
IBssomBufferWriter是基于緩沖區的寫入介面, 提供了寫入行為
| 方法 | 描述 |
|---|---|
| Buffered | 在緩沖區中已寫入位元組的數目 |
| Advance | 用于指示已寫入緩沖區的部分 |
| Position | 寫入器的當前位置 |
| Seek | 設定當前寫入器的位置 |
| SeekWithOutVerify | 設定當前寫入器的位置, 并且不對Buffered的邊界進行驗證 |
| GetRef | 從當前位置獲取用于寫入的位元組序列的參考 |
| CanGetSizeRefForProvidePerformanceInTryWrite | 在欄位編組中, 當前位置是否能提供指定大小的位元組序列參考以用來提供內部某些型別寫入的性能 |
| GetBssomBuffer | 獲取當前寫入器所使用的緩沖區 |
Bssom.Net內部已經對byte[], Stream進行了IBssomBuffer和IBssomBufferWriter介面的封裝, 用戶無需手動封裝
3.格式化器
格式化是Bssom.Net將.Net物件和Bssom格式進行互相轉換的一個程序. Bssom.Net通過IBssomFormatter<T>來實作對物件的格式化.
| API | 描述 |
|---|---|
| Size | 獲取物件被序列化后的大小 |
| Serialize | 將物件序列化成Bssom二進制格式 |
| Deserialize | 將Bssom二進制格式反序列化成物件 |
Bssom.Net內部已經內置了許多格式化器, 如.NET的基元型別, 鍵值對型別, 可迭代型別... 他們在Bssom.Serializer.Formatters命名空間下, 你可以找到它并直接呼叫它.
如果你不需要特殊的處理某個型別的話, 那么這些格式化器基本可以覆寫你的大部分需求. 而如何找到格式化器, 這則是決議器所需要做的.
4.決議器
決議是將.Net型別物件獲取到對應的格式化器的一個程序.Bssom.Net通過IFormatterResolver來實作對物件的決議.
| API | 描述 |
|---|---|
| GetFormatter | 獲取物件的格式化器實體 |
決議器通常具備決議型別和保存格式化器這兩種功能, Bssom.Net中已實作的決議器在內部會對.net型別進行格式化器的查找, 然后通過靜態泛型的特性快取被找到的格式化器, 完成了將一個或一組.net型別系結到對應的格式化器的這樣程序.
IFormatterResolver是Bssom.NET開始對物件序列化的最上層的入口, 他們在Bssom.Serializer.Resolvers命名空間下.
| 名稱 | 描述 |
|---|---|
| PrimitiveResolver | 該決議器提供了sbyte,Int16,Int32,Int64,byte,UInt16,UInt32,UInt64,Single,Double,bool,char,Guid,Decimal,string,DateTime的型別的決議器 |
| AttributeFormatterResolver | 獲取并提供用戶自定義格式化器的實體 |
| BuildInResolver | 提供了StringBuilder,BitArray,DataTable等型別的決議器 |
| BssomValueResolver | 提供了BssomValue型別的決議器 |
| IDictionaryResolver | 獲取和生成具有IDictionary行為的型別的決議器, 該決議器抽象了BCL中對于鍵值對定義的行為規則, 為滿足該規則的物件進行動態決議代碼的生成.在決議器內部, 將通過運行時的配置選項來選擇Map1或Map2的兩種格式 |
| ICollectionResolver | 獲取和生成具有IColloction行為的型別的決議器, 該決議器抽象了BCL中對于收集器定義的行為規則, 為滿足該規則的物件進行動態決議代碼的生成. 在決議器內部, 如果集合中的元素型別為基元型別, 則將其決議成Array1格式, 否則決議為Array2格式 |
| MapCodeGenResolver | 獲取和生成物件的公開欄位和屬性進行BssomMap型別編碼的決議器, 若物件為介面, 則會自動生成該介面的實作作為反序列化的載體.在決議器內部, 始終將型別決議為Map2格式, 且提供Map1和Map2兩種格式的反序列化代碼 |
| ObjectResolver | 提供了Object型別的決議器 |
| CompositedResolver | 復合決議器,組合了Object,Primitive,Attribute,BssomValue,BuildIn,IDictionary,ICollection,MapCodeGen決議器 |
因為
IDictionaryResolver和ICollectionResolver中定義的足夠抽象的規則,Bssom.Net不需要為未來.NET可能出現的新的IDictionary或IColloction實作而撰寫特定的決議代碼.
在Bssom.Net中可以通過BssomSerializerOptions中的FormatterResolver屬性來注入序列化所需要的決議器, 默認為CompositedResolver, CompositedResolver將會對型別依次從 Object,Primitive,Attribute,BssomValue,BuildIn,IDictionary,ICollection,MapCodeGen決議器中進行查找, 直到找到對應的決議器.
5.擴展
讓我們看一下Bssom.Net序列化的程序:
input T -> Call serialize(T) -> Find BssomResolver -> Provide type formatter -> formatter.Serialize(T);
在整個序列化的程序中, 每個步驟都是透明的, 這意味著若用戶對Bssom.Net內部定義的決議器或格式化器不滿意的話, 則可以自己擴展它.
用戶可以自己通過實作IFormatterResolver和IBssomFormatter來替代默認的決議器, 在Bssom.Serializer.Binary.BssomBinaryPrimitives(在即將到來的小版本中將重構該類)和讀寫器本身所暴露的公開API中提供對Bssom格式的低級寫入和讀取實作.
簡單示例可以參考更多可能介紹
6.高級API
BssomSerializer
BssomSerializer是Bssom最上層的API, 在Bssom.Serializer命名空間下, 是Bssom開始作業的入口. 它的靜態方法構成了Bssom.Net的主要API.
| API | 描述 | 多載 |
|---|---|---|
| Size | 在不進行序列化的情況下, 獲取物件被序列化后的二進制資料大小 | (t, option),(ref context, t) |
| Serialize | 將給定的值序列化為Bssom二進制 | (byte[], t, option), (stream, t, option), (IBssomBufWriter, t, option), (ref context, t) |
| Deserialize | 將Bssom二進制資料反序列化成.net物件 | (byte[], option),(stream, option),(IBssomBuf, option),(ref context) |
| SerializeAsync | 異步的序列化給定的值為Bssom二進制 | 同上 |
| DeserializeAsync | 異步的將Bssom二進制資料反序列化成.net物件 | 同上 |
BssomSerializerOptions
BssomSerializer作為最上層的API,我們在呼叫它時,需要傳遞一個可空的BssomSerializerOptions型別的Option引數.
BssomSerializerOptions是Bssom在整個序列化作業期間所需要使用的配置. 默認為BssomSerializerOptions.Default.
- FormatterResolver : 在Option中,你可以為
FormatterResolver注冊決議器, 如果沒有手動注冊, 則使用默認的CompositedResolver, Bssom將總是通過FormatterResolver來對型別進行決議. - Security : 這是用于序列化期間的安全相關選項, 目前僅提供了在反序列化期間對深度的驗證,默認為 不限制
- IsPriorityToDeserializeObjectAsBssomValue : 該選項決定了反序列化時是否將Object型別轉換為BssomValue型別, 如果為
false, 則默認反序列化為原生型別. 默認為false. - IsUseStandardDateTime : Bssom.Net對
DateTime型別實作了標準的Bssom協議Unix格式 和 .NET平臺的本地格式, 本地格式具有更少的位元組, 但不具備和其它平臺的互動性, 默認為false. - IDictionaryIsSerializeMap1Type : 此選項決定了對具有
IDictionary行為的型別默認使用哪種格式進行序列化, 如果為true則使用Map1格式, 否則為Map2格式. 默認為true
BssomSerializeContext
BssomSerializeContext提供了序列化期間所使用的背景關系資訊, 這其中也包括了BssomSerializerOptions
- BssomSerializerOptions : 序列化期間所使用的配置資訊
- ContextDataSlots : 提供了一個資料槽, 供用戶在序列化期間自己存盤和讀取的一個存盤介質
- CancellationToken : 一個序列化操作取消的標記, 用戶可以中途取消正在進行的序列化操作
7.欄位編組
Bssom.Net擁有讀取欄位而不用完全反序列化和更改值而不用完全序列化功能, 這是因為Bssom協議有著良好的結構化特征, 在Bssom.Net的實作里, 這樣的功能則暴露在BssomFieldMarshaller中.
BssomFieldMarshaller
BssomFieldMarshaller提供一套API用于對被序列化后的資料進行更低粒度的控制.
| API | 描述 |
|---|---|
| IndexOf | 通過特殊的輸入格式來獲取被指定的物件在Bssom二進制中的位置,回傳偏移量資訊 |
| ReadValue | 通過指定的偏移量資訊來讀取整個元素 |
| ReadValueType | 通過指定的偏移量資訊僅讀取元素型別 |
| ReadValueTypeCode | 通過指定的偏移量資訊僅讀取元素型別的二進制碼 |
| ReadValueSize | 通過指定的偏移量資訊來獲取元素在Bssom二進制中所存盤的大小 |
| ReadArrayCountByMapType | 通過指定的偏移量資訊來讀取BssomArray的元素數量 |
| ReadAllKeysByMapType | 通過指定的偏移量資訊來讀取BssomMap中的元資料(包含Key和值的偏移量) |
| TryWrite | 通過指定的偏移量資訊在Bssom二進制中重新對值進行寫入, 若寫入值的寬度大于被寫入槽的寬度,則失敗 |
每種方法都提供了 byte[] 和 IBssomBuf 的多載
簡單欄位訪問語言
Bssom.Net為IndexOf定義了一種簡單的欄位訪問語言, 該語言共定義了兩種訪問形式, 一種是訪問Map型別(該Map型別的鍵必須為String型別), 一種是訪問Array型別. 兩種訪問形式可以自由組合.
- [Key] : 代表通過
Key來訪問Map型別的值, 輸入的Key只表示String型別 - $Index : 代表通過下標來訪問
Array型別的元素, 輸入的Index只能是整數型別
假設有如下資料
{
"Postcodes" : {
"WuHan" : [430070,430071,430072,430073],
"XiangYang" : [441000,441001,441002]
},
"Province" : "HuBei"
}
可以通過如下方式進行元素訪問, 在示例中可以了解更多細節
[Postcodes][WuHan]$1 => 4330071
[Province] => "HuBei"
自定義欄位訪問形式介面
Bssom.Net為IndexOf提供了IIndexOfInputSource介面用來接收自定義的欄位訪問源, 使用該介面后Map型別的Key將不再受限制, Key可以為任意輸入型別.
IndexOfObjectsInputSource 是 Bssom.Net為用戶提供的IIndexOfInputSource介面的通用實作. 它接收一組可迭代的物件,當呼叫IndexOf的時候, 將依次對物件進行迭代.
假設有如下資料
{
2018-01-01 : {
0 : ["Rain1","Rain2","Rain3"],
4 : ["Rain4","Fair5","Fair6"]
}
}
可以通過如下方式進行元素訪問, 在示例中可以了解更多細節
new IndexOfObjectsInputSource(new Entry[]{
new Entry(DateTime.Parse("2018-01-01"),ValueIsMapKey: true),
new Entry(3,ValueIsMapKey: true),
new Entry(1,ValueIsMapKey: false),
})
output => "Fair5"
8.動態代碼生成
Bssom.Net對IDictionaryResolver, ICollectionResolver, MapCodeGenResolver, ObjectResolver 使用了動態代碼生成技術, 通過運算式樹和Emit共同生成運行時代碼, 如果應用程式是純AOT環境, 則將不支持.
在MapCodeGenResolver中對Map1型別的反序列化使用了以8位元組(64位字長)為單位的類前綴樹的自動機查找模式, 這是非常有效且快速的方式, 它避免了對字串進行完全Hash運算以及字符比較開銷, 通過對MapCodeGenResolver.Save()方法你將看到這些自動生成的代碼.

MapCodeGenResolver中對Map2型別的反序列化則使用了內置的Bssom協議的Map格式查找代碼,該代碼是狀態機模式撰寫, 分為快速和低速版, 這取決于讀取器是否能夠提供 TryReadFixedRef.

另外,對于Size方法,MapCodeGenResolver的處理也是非常快速的,因為它已經提前計算好了元資料的大小,并且行內了基元欄位本身的固定大小.

9.特性
Bssom.Net中目前擁有5個特性.
- AliasAttribute : 別名特性, 用于修改Map格式物件欄位在二進制中所保存的欄位名稱
- BssomFormatterAttribute : 自定義格式化特性, 當欄位屬性或型別被該特性標記后, 此型別的格式化將采用該特性所指定的格式化器
- IgnoreKeyAttribute : 忽略某一個Key, 序列化時將忽略被標記的欄位, 適用于Map格式
- OnlyIncludeAttribute : 僅包含某一個Key, 序列化時僅包含該Key, 適用于Map格式, 與
IgnoreKeyAttribute作用相反,優先級更高 - SerializationConstructorAttribute : 為型別的反序列化指定一個建構式
10.更多的可能性
你可以自己撰寫決議器, 撰寫格式化器, 也可以定義你自己的特性, 也可以封裝用于序列化的Option, 并且Bssom.Net還提供了背景關系資料槽的支持, 這可以讓序列化行為變得多樣性.
如果你能為Bssom.Net提供有用或者側重于高性能的擴展包, 那么請您告訴我.
下面示例撰寫了以String型別為原型的決議器, 該決議器通過與背景關系互動的方式來帶來字串型別序列化性能的提升.
public sealed class MyStringFormatterResolver : IFormatterResolver
{
public static MyStringFormatterResolver Instance = new MyStringFormatterResolver();
public IBssomFormatter<T> GetFormatter<T>()
{
return FormatterCache<T>.Formatter;
}
private static class FormatterCache<T>
{
public static readonly IBssomFormatter<T> Formatter;
static FormatterCache()
{
if (typeof(T) == typeof(string))
Formatter = (IBssomFormatter<T>)(object)MyStringFormatter.Instance;
}
}
}
public sealed class MyStringFormatter : IBssomFormatter<string>
{
public static MyStringFormatter Instance = new MyStringFormatter();
public string Deserialize(ref BssomReader reader, ref BssomDeserializeContext context)
{
if (reader.TryReadNull())
{
return null;
}
reader.EnsureType(BssomType.StringCode);
int dataLen = reader.ReadVariableNumber();
ref byte refb = ref reader.BssomBuffer.ReadRef((int)dataLen);
fixed (byte* pRefb = &refb)
{
return new string((sbyte*)pRefb, 0, (int)dataLen, UTF8Encoding.UTF8);
}
}
public void Serialize(ref BssomWriter writer, ref BssomSerializeContext context, string value)
{
if (value =https://www.cnblogs.com/1996V/archive/2020/10/27/= null)
{
writer.WriteNull();
return;
}
int valueUtf8Size = context.ContextDataSlots.PopMyStringSize();
writer.WriteBuildInType(BssomType.StringCode);
writer.WriteVariableNumber(valueUtf8Size);
ref byte refb = ref writer.BufferWriter.GetRef(valueUtf8Size);
fixed (char* pValue = value)
fixed (byte* pRefb = &refb)
{
UTF8Encoding.UTF8.GetBytes(pValue, value.Length, pRefb, valueUtf8Size);
}
writer.BufferWriter.Advance(valueUtf8Size);
}
public int Size(ref BssomSizeContext context, string value)
{
if (value == null)
return BssomBinaryPrimitives.NullSize;
int dataSize = UTF8Encoding.UTF8.GetByteCount(value);
context.ContextDataSlots.PushMyStringSize(dataSize);
return BssomBinaryPrimitives.BuildInTypeCodeSize + dataSize;
}
}
public void MyTest()
{
var option = BssomSerializerOptions.Default.WithFormatterResolver(MyStringFormatterResolver.Instance);
string str = RandomHelper.RandomValue<string>();
BssomSizeContext sizeContext = new BssomSizeContext(option);
int len = BssomSerializer.Size(ref sizeContext, str);
if (len > 1000)
throw new Exception("Size of value storage binary exceeded");
BssomSerializeContext serContext = new BssomSerializeContext(option);
sizeContext.ContextDataSlots.SetMyStringStack(serContext.ContextDataSlots);
var bytes = BssomSerializer.Serialize(ref serContext, str);
var deStr = BssomSerializer.Deserialize<string>(bytes);
Assert.Equal(str,deStr);
}
上面的代碼是單獨為String定義了一個新的決議器和新的格式化器, 該格式化器可以將Size方法中對字串計算的UTF8大小存盤在背景關系中, 這樣在序列化時不用重復對String再做一次UTF8大小計算.
11.如何使用
Bssom.Net是無合約的, 開箱即用, 這里有些示例代碼.
Size
BssomSerializer.Size 方法用于 獲取物件被序列化后的二進制資料大小,高性能的內部實作,幾乎無開銷
//獲取值被序列化后的大小
object value = https://www.cnblogs.com/1996V/archive/2020/10/27/RandomHelper.RandomValue
//使用背景關系獲取值被序列化后的大小
BssomSizeContext context = new BssomSizeContext(BssomSerializerOptions.Default);
object value = https://www.cnblogs.com/1996V/archive/2020/10/27/RandomHelper.RandomValue
Serialize
BssomSerializer.Serialize 方法用于 將給定的值序列化為Bssom二進制,高性能的內部實作,以下是部分常用方法,每個方法都擁有CancellationToken的多載
//直接對物件進行序列化,將回傳一個被序列化后的位元組陣列
object value = https://www.cnblogs.com/1996V/archive/2020/10/27/RandomHelper.RandomValue
//將物件序列化到指定的位元組陣列中,若容量不夠將自動擴容,最侄訓傳序列化的位元組數
object value = https://www.cnblogs.com/1996V/archive/2020/10/27/RandomHelper.RandomValue
//將物件序列化到自定義的寫入器中
object value = https://www.cnblogs.com/1996V/archive/2020/10/27/RandomHelper.RandomValue
//使用序列化背景關系進行序列化
object value = https://www.cnblogs.com/1996V/archive/2020/10/27/RandomHelper.RandomValue
//將物件序列化到流中
object value = https://www.cnblogs.com/1996V/archive/2020/10/27/RandomHelper.RandomValue
//異步的將物件序列化到流中
object value = https://www.cnblogs.com/1996V/archive/2020/10/27/RandomHelper.RandomValue
Deserialize
BssomSerializer.Deserialize 方法用于 將給定的Bssom緩沖區反序列化為物件,高性能的內部實作,以下是部分常用方法,每個方法都擁有CancellationToken的多載
//從給定的位元組陣列中反序列化物件
byte[] buf = remote();
T value = https://www.cnblogs.com/1996V/archive/2020/10/27/BssomSerializer.Deserialize(buf, 0, out int readSize, option: BssomSerializerOptions.Default);
//從給定的buffer中反序列化物件
IBssomBuffer buffer = remote();
object value = https://www.cnblogs.com/1996V/archive/2020/10/27/BssomSerializer.Deserialize
//使用背景關系從給定的buffer中反序列化物件
BssomDeserializeContext context = new BssomDeserializeContext(BssomSerializerOptions.Default);
IBssomBuffer buffer = remote();
object value = https://www.cnblogs.com/1996V/archive/2020/10/27/BssomSerializer.Deserialize
//從流中反序列化物件
Stream stream = remote();
object value = https://www.cnblogs.com/1996V/archive/2020/10/27/BssomSerializer.Deserialize
//異步的從流中反序列化物件
Stream stream = remote();
object value = https://www.cnblogs.com/1996V/archive/2020/10/27/await BssomSerializer.DeserializeAsync
//傳遞一個Type, 從流中反序列化物件為指定的Type型別
Stream stream = remote();
Type type = typeof(class);
object value = https://www.cnblogs.com/1996V/archive/2020/10/27/BssomSerializer.Deserialize(stream, type, option: BssomSerializerOptions.Default);
//傳遞一個Type, 異步的從流中反序列化物件為指定的Type型別
Stream stream = remote();
Type type = typeof(class);
object value = https://www.cnblogs.com/1996V/archive/2020/10/27/await BssomSerializer.DeserializeAsync(stream, type, option: BssomSerializerOptions.Default);
ReadValue
BssomFieldMarshaller.ReadValue 方法用于 在二進制資料中僅讀取某一個值,如果你只想讀取物件中的某一個值,而不用完整的反序列化它,那么這個方法非常有用
//通過內嵌的簡單欄位訪問語言,獲取Dict中的一個Key對應的值
var val = new Dictionary<string, object>() {
{ "A",(int)3},
{ "B",(DateTime)DateTime.MaxValue},
};
var buf = BssomSerializer.Serialize(val);
var bsfm = new BssomFieldMarshaller(buf);
BssomFieldOffsetInfo fieldOffInfo = bsfm.IndexOf("[A]")
bsfm.ReadValue<int>(fieldOffInfo).Is(3);
//通過內嵌的簡單欄位訪問語言,獲取class中的一個屬性的值
var val = new MyClass() {
Name = "bssom",
Nature = "Binary"
};
var buf = BssomSerializer.Serialize(val);
var bsfm = new BssomFieldMarshaller(buf);
BssomFieldOffsetInfo fieldOffInfo = bsfm.IndexOf("[Name]")
bsfm.ReadValue<string>(fieldOffInfo).Is("bssom");
//通過內嵌的簡單欄位訪問語言,獲取陣列中的一個屬性的值
var val = new object[] { (int)1,(double)2.2 }
var buf = BssomSerializer.Serialize(val);
var bsfm = new BssomFieldMarshaller(buf);
BssomFieldOffsetInfo fieldOffInfo = bsfm.IndexOf("$1")
bsfm.ReadValue<double>(fieldOffInfo).Is((double)2.2);
//通過內嵌的簡單欄位訪問語言,組合獲取一個物件
var val = new MyClass() {
Name = "bssom",
Nature = "Binary",
Data = https://www.cnblogs.com/1996V/archive/2020/10/27/new int[] { 3, 2, 1}
};
var buf = BssomSerializer.Serialize(val);
var bsfm = new BssomFieldMarshaller(buf);
BssomFieldOffsetInfo fieldOffInfo = bsfm.IndexOf("[Data]$1")
bsfm.ReadValue<int>(fieldOffInfo).Is(2);
//通過自定義的欄位訪問形式,組合獲取一個物件
var val = new Dictionary<object, object>() {
{ DateTime.Parse("2018-01-01"), new object[]{'A','B'} },
{ "Charec",(DateTime)DateTime.MaxValue},
};
var buf = BssomSerializer.Serialize(val);
var bsfm = new BssomFieldMarshaller(buf);
IIndexOfInputSource input = new IndexOfObjectsInputSource(new Entry[]{
new Entry(DateTime.Parse("2018-01-01"),ValueIsMapKey: true),
new Entry(1,ValueIsMapKey: false),
})
BssomFieldOffsetInfo fieldOffInfo = bsfm.IndexOf(input)
bsfm.ReadValue<int>(fieldOffInfo).Is('B');
ReadAllMapKeys
BssomFieldMarshaller.ReadAllMapKeys 方法用于 在二進制資料中讀取Map格式的所有Key和值偏移量,如果你想了解該二進制資料中的鍵值情況,但又不想完全讀取它,那么這個方法非常有用.
var val = new Dictionary<object, object>(){
{ "Id" , 1 },
{ "Path" , "../t.jpg" },
{ "Data" , new byte[3000] }
};
var buf = BssomSerializer.Serialize(val);
var bsfm = new BssomFieldMarshaller(buf);
bsfm.ReadAllMapKeys<object>(BssomFieldOffsetInfo.Zero).Print();
//output
// line 1: BssomString::"Id", BssomFieldOffsetInfo
// line 2: BssomString::"Path", BssomFieldOffsetInfo
// line 3: BssomString::"Data", BssomFieldOffsetInfo
TryWriteValue
BssomFieldMarshaller.TryWriteValue 方法用于 對二進制資料的值進行修改,當你只想修改物件中的某個值,而不用重新序列化整個物件時,那么這個方法非常有用
//修改字串物件
var val = "abcd";
var buf = BssomSerializer.Serialize(val);
var bsfm = new BssomFieldMarshaller(buf);
bsfm.TryWrite(BssomFieldOffsetInfo.Zero, "abc");
string upVal = BssomSerializer.Deserialize<string>(buf);
upVal.Is("abc");
//修改IDict物件中的某個鍵
var val = new Dictionary<string, object>(){
{ "Id" , 1 },
{ "Path" , "../t.jpg" },
{ "Data" , new byte[3000] }
};
var buf = BssomSerializer.Serialize(val);
var bsfm = new BssomFieldMarshaller(buf);
bsfm.TryWrite(bsfm.IndexOf("[Id]"), 3);
var upVal = BssomSerializer.Deserialize<Dictionary<string, object>>(buf);
upVal["Id"].Is(3);
//修改IDict物件中的某個鍵
var val = new MyClass() {
Name = "bssom",
Nature = "Binary",
Data = https://www.cnblogs.com/1996V/archive/2020/10/27/new int[] { 3, 2, 1}
};
var buf = BssomSerializer.Serialize(val);
var bsfm = new BssomFieldMarshaller(buf);
bsfm.TryWrite(bsfm.IndexOf("[Name]"), "zz");
var upVal = BssomSerializer.Deserialize<MyClass>(buf);
upVal["Name"].Is("zz");
//修改Array物件中的某個元素
var val = new object[] { "abc" , 37 };
var buf = BssomSerializer.Serialize(val);
var bsfm = new BssomFieldMarshaller(buf);
bsfm.TryWrite(bsfm.IndexOf("$1"), 40);
var upVal = BssomSerializer.Deserialize<MyClass>(buf);
((int)upVal[1]).Is(40);
//組合修改物件中的某個元素
var val = new object[] {
22,
37,
new MyClass() {
Name = "bssom",
Nature = "Binary",
Data = https://www.cnblogs.com/1996V/archive/2020/10/27/new int[] { 3, 2, 1}
}
};
var buf = BssomSerializer.Serialize(val);
var bsfm = new BssomFieldMarshaller(buf);
bsfm.TryWrite(bsfm.IndexOf("$2[Name]"), "zz");
var upVal = BssomSerializer.Deserialize<MyClass>(buf);
((MyClass)upVal[1]).Name.Is("zz");
如何使用特性
如何定義擴展
12.如何參與專案貢獻
如果你想參與本專案的發展,那么我將非常榮幸和高興,歡迎Fork或Pull Request,也可以加入QQ群976304396來進行開源技術的探討
點擊加入群聊.NET開源技術交流群 禁水,只能聊技術
13.誰在使用
- BssomDB(即將開源) 一個使用Bssom協議的純C#的嵌入式事務型檔案資料庫
14.其它
我喜歡和我一樣的人交朋友,不被環境影響,自己是自己的老師,歡迎加群 Net開源技術交流群 976304396 ,與我討論與性能相關的話題!
想了解我日常是怎樣寫代碼的嗎? 歡迎關注我的抖音賬號: 198152455 .
作者:小曾
出處:https://www.cnblogs.com/1996V/p/13884968.html 歡迎轉載,但請保留以上完整文章,在顯要地方顯示署名以及原文鏈接,
Net開源技術交流群 976304396 , 抖音賬號: 198152455
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/193856.html
標籤:.NET技术
