開源!一款功能強大的高性能二進制序列化器Bssom.Net

很久沒更新博客了,我開源了一款高性能的二進制序列化器Bssom.Net和新穎的二進制協議Bssom,歡迎你們Star,歡迎參與項目貢獻!

Net開源技術交流羣 976304396,禁止水,只能討論技術, 歡迎與我討論和性能相關的技術話題!
另外,我還在抖音申請了一個帳號,用來記錄本身的平常生活, 想了解我日常是怎麼寫代碼的嗎? 來關注我一下,哈哈! 抖音號: 198152455
Bssom.Net項目地址: https://github.com/1996v/Bssom.Net
Bssom協議地址: https://github.com/1996v/Bssomhtml

A small, high performance, powerful serializer using bssom binary protocol

Nuget

Bssom.Net是一個使用bssom結構協議實現的高性能結構化二進制序列化器,它具備如下特色,小巧,快速,功能性強.git

  1. 小巧,文件僅300多k
  2. 快速,它具備一流的序列化和反序列化性能
  3. 功能性強:
    • 能夠獲取對象被序列化後的大小而不用完整序列化對象
    • 能夠讀取對象中的某個元素而不用完整的反序列化對象
    • 能夠更改對象中的某個元素而不用完整的序列化
    • 序列化後的格式具備自描述性

爲何須要?

目前c#已經有不少二進制序列化器, 但這些序列化器都只提供了單一的序列化和反序列化功能.github

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.性能


這裏是與.NET平臺下很是優秀的兩款序列化程序(MessagePackProtobuf-net)進行性能比較的基準.編程

柱狀數據表明執行相同任務所花費的時間, 越低表明性能越快, 折線數據表明執行相同任務所產生的GC, 越低表明在執行中所產生的垃圾越少 , 從性能比較結果能夠看出Bssom.Net的性能是很是優異的.c#

Bssom.Net使用不少技術來提升性能.api

  • 使用內存池技術, 用於寫入的內存能夠複用
  • 使用表達式和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進行了IBssomBufferIBssomBufferWriter接口的封裝, 用戶無需手動封裝

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中對於鍵值對定義的行爲規則, 爲知足該規則的對象進行動態解析代碼的生成.在解析器內部, 將經過運行時的配置選項來選擇Map1Map2的兩種格式
ICollectionResolver 獲取和生成具備IColloction行爲的類型的解析器, 該解析器抽象了BCL中對於收集器定義的行爲規則, 爲知足該規則的對象進行動態解析代碼的生成. 在解析器內部, 若是集合中的元素類型爲基元類型, 則將其解析成Array1格式, 不然解析爲Array2格式
MapCodeGenResolver 獲取和生成對象的公開字段和屬性進行BssomMap類型編碼的解析器, 若對象爲接口, 則會自動生成該接口的實現做爲反序列化的載體.在解析器內部, 始終將類型解析爲Map2格式, 且提供Map1Map2兩種格式的反序列化代碼
ObjectResolver 提供了Object類型的解析器
CompositedResolver 複合解析器,組合了Object,Primitive,Attribute,BssomValue,BuildIn,IDictionary,ICollection,MapCodeGen解析器

由於IDictionaryResolverICollectionResolver中定義的足夠抽象的規則,Bssom.Net不須要爲將來.NET可能出現的新的IDictionaryIColloction實現而編寫特定的解析代碼.

在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內部定義的解析器或格式化器不滿意的話, 則能夠本身擴展它.

用戶能夠本身經過實現IFormatterResolverIBssomFormatter替代默認的解析器, 在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 == 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 = RandomHelper.RandomValue<object>();
int size = BssomSerializer.Size(value, option: BssomSerializerOptions.Default);
//使用上下文獲取值被序列化後的大小
BssomSizeContext context = new BssomSizeContext(BssomSerializerOptions.Default);
object value = RandomHelper.RandomValue<object>();
int size = BssomSerializer.Size(ref context, value);

Serialize

BssomSerializer.Serialize 方法用於 將給定的值序列化爲Bssom二進制,高性能的內部實現,如下是部分經常使用方法,每一個方法都擁有CancellationToken的重載

//直接對對象進行序列化,將返回一個被序列化後的字節數組
object value = RandomHelper.RandomValue<object>();
byte[] binary = BssomSerializer.Serialize(value, option: BssomSerializerOptions.Default);
//將對象序列化到指定的字節數組中,若容量不夠將自動擴容,最終返回序列化的字節數
object value = RandomHelper.RandomValue<object>();
byte[] buf = local();
int serializeSize = BssomSerializer.Serialize(ref buf, 0, value, option: BssomSerializerOptions.Default);
//將對象序列化到自定義的寫入器中
object value = RandomHelper.RandomValue<object>();
IBssomBufferWriter writer = new Impl();
BssomSerializer.Serialize(value, writer, option: BssomSerializerOptions.Default);
//使用序列化上下文進行序列化
object value = RandomHelper.RandomValue<object>();
BssomSerializeContext context = new BssomSerializeContext(BssomSerializerOptions.Default);
byte[] binary = BssomSerializer.Serialize(ref context, value);
//將對象序列化到流中
object value = RandomHelper.RandomValue<object>();
Stream stream = new MemoryStream();
BssomSerializer.Serialize(stream, value, option: BssomSerializerOptions.Default);
//異步的將對象序列化到流中
object value = RandomHelper.RandomValue<object>();
Stream stream = new MemoryStream();
await BssomSerializer.SerializeAsync(stream, value, option: BssomSerializerOptions.Default);

Deserialize

BssomSerializer.Deserialize 方法用於 將給定的Bssom緩衝區反序列化爲對象,高性能的內部實現,如下是部分經常使用方法,每一個方法都擁有CancellationToken的重載

//從給定的字節數組中反序列化對象
byte[] buf = remote();
T value = BssomSerializer.Deserialize<T>(buf, 0, out int readSize, option: BssomSerializerOptions.Default);
//從給定的buffer中反序列化對象
IBssomBuffer buffer = remote();
object value = BssomSerializer.Deserialize<object>(buffer, option: BssomSerializerOptions.Default);
//使用上下文從給定的buffer中反序列化對象
BssomDeserializeContext context = new BssomDeserializeContext(BssomSerializerOptions.Default);
IBssomBuffer buffer = remote();
object value = BssomSerializer.Deserialize<object>(ref context, buffer);
//從流中反序列化對象
Stream stream = remote();
object value = BssomSerializer.Deserialize<object>(stream, option: BssomSerializerOptions.Default);
//異步的從流中反序列化對象
Stream stream = remote();
object value = await BssomSerializer.DeserializeAsync<object>(stream, option: BssomSerializerOptions.Default);
//傳遞一個Type, 從流中反序列化對象爲指定的Type類型
Stream stream = remote();
Type type = typeof(class);
object value = BssomSerializer.Deserialize(stream, type, option: BssomSerializerOptions.Default);
//傳遞一個Type, 異步的從流中反序列化對象爲指定的Type類型
Stream stream = remote();
Type type = typeof(class);
object value = 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 = 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 = 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 = 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 .

做者:小曾
出處:http://www.javashuo.com/article/p-rtejcbun-nu.html 歡迎轉載,但請保留以上完整文章,在顯要地方顯示署名以及原文連接。 Net開源技術交流羣 976304396 , 抖音帳號: 198152455

相關文章
相關標籤/搜索