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

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

2020-10-27 17:43:28 .NET開發

好久沒更新博客了,我開源了一款高性能的二進制序列化器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

Nuget

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

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

為什么需要?

目前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進行了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 =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();
int size = BssomSerializer.Size(value, option: BssomSerializerOptions.Default);

//使用背景關系獲取值被序列化后的大小
BssomSizeContext context = new BssomSizeContext(BssomSerializerOptions.Default);
object value = https://www.cnblogs.com/1996V/archive/2020/10/27/RandomHelper.RandomValue();
int size = BssomSerializer.Size(ref context, value);

Serialize

BssomSerializer.Serialize 方法用于 將給定的值序列化為Bssom二進制,高性能的內部實作,以下是部分常用方法,每個方法都擁有CancellationToken的多載

//直接對物件進行序列化,將回傳一個被序列化后的位元組陣列
object value = https://www.cnblogs.com/1996V/archive/2020/10/27/RandomHelper.RandomValue();
byte[] binary = BssomSerializer.Serialize(value, option: BssomSerializerOptions.Default);

//將物件序列化到指定的位元組陣列中,若容量不夠將自動擴容,最侄訓傳序列化的位元組數
object value = https://www.cnblogs.com/1996V/archive/2020/10/27/RandomHelper.RandomValue();
byte[] buf = local();
int serializeSize = BssomSerializer.Serialize(ref buf, 0, value, option: BssomSerializerOptions.Default);

//將物件序列化到自定義的寫入器中
object value = https://www.cnblogs.com/1996V/archive/2020/10/27/RandomHelper.RandomValue();
IBssomBufferWriter writer = new Impl();
BssomSerializer.Serialize(value, writer, option: BssomSerializerOptions.Default);

//使用序列化背景關系進行序列化
object value = https://www.cnblogs.com/1996V/archive/2020/10/27/RandomHelper.RandomValue();
BssomSerializeContext context = new BssomSerializeContext(BssomSerializerOptions.Default);
byte[] binary = BssomSerializer.Serialize(ref context, value);

//將物件序列化到流中
object value = https://www.cnblogs.com/1996V/archive/2020/10/27/RandomHelper.RandomValue();
Stream stream = new MemoryStream();
BssomSerializer.Serialize(stream, value, option: BssomSerializerOptions.Default);

//異步的將物件序列化到流中
object value = https://www.cnblogs.com/1996V/archive/2020/10/27/RandomHelper.RandomValue();
Stream stream = new MemoryStream();
await BssomSerializer.SerializeAsync(stream, value, option: BssomSerializerOptions.Default);

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, option: BssomSerializerOptions.Default);

//使用背景關系從給定的buffer中反序列化物件
BssomDeserializeContext context = new BssomDeserializeContext(BssomSerializerOptions.Default);
IBssomBuffer buffer = remote();
object value = https://www.cnblogs.com/1996V/archive/2020/10/27/BssomSerializer.Deserialize(ref context, buffer);

//從流中反序列化物件
Stream stream = remote();
object value = https://www.cnblogs.com/1996V/archive/2020/10/27/BssomSerializer.Deserialize(stream, option: BssomSerializerOptions.Default);

//異步的從流中反序列化物件
Stream stream = remote();
object value = https://www.cnblogs.com/1996V/archive/2020/10/27/await BssomSerializer.DeserializeAsync(stream, option: BssomSerializerOptions.Default);

//傳遞一個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技术

上一篇:dynamics 365 彈框提示需要授權

下一篇:C# 9.0 新特性預覽 - init-only 屬性

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more