플랫 버퍼(FlatBuffers)란?
FlatBuffers는 C,C++,C#,GO,Java, JavaScript, Lobster, Lua, TypeScript, PHP, Python, Rust를 위한 크로스 플랫폼 직렬화 라이브러리이다.
플랫 버퍼의 특징
- 패킹 / 언패킹 없이 직렬화된 데이터에 엑세스
- 메모리 효율성 및 속도 증가
- 생성된 코드가 작고 단일 Header 파일로 쉽게 통합 가능
- 모든 언어에서 사용하기 편리하게 제공
- 크로스 플랫폼, 종속성 없이 사용이 가능
플랫 버퍼 다운로드 주소
DLL 파일 빌드
C# 프로젝트 파일에서 임포트할 DLL 파일을 빌드해줍니다.
플랫 버퍼를 다운 받은 폴더에서
\flatbuffers-23.5.26\flatbuffers-23.5.26\net\FlatBuffers
C# 을 위해 준비된 경로로 이동해서 터미널을 열고 아래의 내용을 입력해줍니다.
dotnet build -f netstandard2.0 "Google.FlatBuffers.csproj"
그러면 정상적으로 dll이 생성됩니다. 해당 결과물을 C# 프로젝트에서 참조하여 사용합니다.
C# 프로젝트에 종속성 추가
프로젝트 오른쪽 클릭 -> 추가 -> COM 참조 추가
위에서 빌드한 Google.FlatBuffers.dll 파일을 추가
플랫 버퍼 스키마 작성
Monster.fbs 파일 생성
// Example IDL file for our monster's schema.
enum Color:byte { Red = 0, Green, Blue = 2 }
union Equipment { Weapon } // Optionally add more tables.
struct Vec3 {
x:float;
y:float;
z:float;
}
table Monster {
pos:Vec3; // Struct.
mana:short = 150;
hp:short = 100;
name:string;
friendly:bool = false (deprecated);
inventory:[ubyte]; // Vector of scalars.
color:Color = Blue; // Enum.
weapons:[Weapon]; // Vector of tables.
equipped:Equipment; // Union.
path:[Vec3]; // Vector of structs.
}
table Weapon {
name:string;
damage:short;
}
root_type Monster;
flatc.exe와 같은 폴더에 두고 컴파일을 진행하자.
fbs 파일 컴파일
컴파일 커맨드
flatc [ GENERATOR OPTIONS ] [ -o PATH ] [ -I PATH ] [ -S ] FILES...
[ -- FILES...]
./flatc --csharp Monster.fbs
예제 코드
using System;
using Google.FlatBuffers;
namespace FlatBufferExample
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
var builder = new FlatBufferBuilder(1);
// Create some weapons for our Monster ('Sword' and 'Axe').
var weapon1Name = builder.CreateString("Sword");
var weapon1Damage = 3;
var weapon2Name = builder.CreateString("Axe");
var weapon2Damage = 5;
// Use the `CreateWeapon()` helper function to create the weapons, since we set every field.
var weaps = new Offset<Weapon>[2];
weaps[0] = Weapon.CreateWeapon(builder, weapon1Name, (short)weapon1Damage);
weaps[1] = Weapon.CreateWeapon(builder, weapon2Name, (short)weapon2Damage);
// Serialize the FlatBuffer data.
var name = builder.CreateString("Orc");
var treasure = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var inv = Monster.CreateInventoryVector(builder, treasure);
var weapons = Monster.CreateWeaponsVector(builder, weaps);
var pos = Vec3.CreateVec3(builder, 1.0f, 2.0f, 3.0f);
Monster.StartMonster(builder);
Monster.AddPos(builder, pos);
Monster.AddHp(builder, (short)300);
Monster.AddName(builder, name);
Monster.AddInventory(builder, inv);
Monster.AddColor(builder, Color.Red);
Monster.AddWeapons(builder, weapons);
Monster.AddEquippedType(builder, Equipment.Weapon);
Monster.AddEquipped(builder, weaps[1].Value);
var orc = Monster.EndMonster(builder);
builder.Finish(orc.Value); // You could also call `Monster.FinishMonsterBuffer(builder, orc);`.
// get byte[] packet
var packet = builder.SizedByteArray();
// We now have a FlatBuffer that we could store on disk or send over a network.
// ...Code to store to disk or send over a network goes here...
// Instead, we are going to access it right away, as if we just received it.
var bb = new ByteBuffer(packet);
// Get access to the root:
var monster = Monster.GetRootAsMonster(bb);
// For C#, unlike other languages, most values (except for vectors and unions) are available as
// properties instead of accessor methods.
// Note: We did not set the `Mana` field explicitly, so we get back the default value.
Assert(monster.Mana == 150, "monster.Mana", Convert.ToString(monster.Mana),
Convert.ToString(150));
Assert(monster.Hp == 300, "monster.Hp", Convert.ToString(monster.Hp), Convert.ToString(30));
Assert(monster.Name.Equals("Orc", StringComparison.Ordinal), "monster.Name", monster.Name,
"Orc");
Assert(monster.Color == Color.Red, "monster.Color", Convert.ToString(monster.Color),
Convert.ToString(Color.Red));
var vec = monster.Pos.Value;
Assert(vec.X == 1.0f, "vec.X",
Convert.ToString(vec.X), Convert.ToString(1.0f));
Assert(vec.Y == 2.0f, "vec.Y",
Convert.ToString(vec.Y), Convert.ToString(2.0f));
Assert(vec.Z == 3.0f, "vec.Z",
Convert.ToString(vec.Z), Convert.ToString(3.0f));
// Get and test the `Inventory` FlatBuffer `vector`.
for (int i = 0; i < monster.InventoryLength; i++)
{
Assert(monster.Inventory(i) == i, "monster.Inventory",
Convert.ToString(monster.Inventory(i)), Convert.ToString(i));
}
// Get and test the `Weapons` FlatBuffer `vector` of `table`s.
var expectedWeaponNames = new string[] { "Sword", "Axe" };
var expectedWeaponDamages = new short[] { 3, 5 };
for (int i = 0; i < monster.WeaponsLength; i++)
{
Assert(monster.Weapons(i).Value.Name.Equals(expectedWeaponNames[i], StringComparison.Ordinal),
"monster.Weapons", monster.Weapons(i).Value.Name, expectedWeaponNames[i]);
Assert(monster.Weapons(i).Value.Damage == expectedWeaponDamages[i], "monster.GetWeapons",
Convert.ToString(monster.Weapons(i).Value.Damage),
Convert.ToString(expectedWeaponDamages[i]));
}
// Get and test the `Equipped` FlatBuffer `union`.
Assert(monster.EquippedType == Equipment.Weapon, "monster.EquippedType",
Convert.ToString(monster.EquippedType), Convert.ToString(Equipment.Weapon));
var equipped = monster.Equipped<Weapon>().Value;
Assert(equipped.Name.Equals("Axe", StringComparison.Ordinal), "equipped.Name", equipped.Name,
"Axe");
Assert(equipped.Damage == 5, "equipped.Damage", Convert.ToString(equipped.Damage),
Convert.ToString(5));
Console.WriteLine("The FlatBuffer was successfully created and verified!");
}
// A helper function to handle assertions.
static void Assert(bool assertPassed, string codeExecuted, string actualValue,
string expectedValue)
{
if (assertPassed == false)
{
Console.WriteLine("Assert failed! " + codeExecuted + " (" + actualValue +
") was not equal to " + expectedValue + ".");
System.Environment.Exit(1);
}
}
}
}
참고 사이트
'프로그래밍' 카테고리의 다른 글
카르노맵(Karnaugh-Map)에 대해서 (29) | 2023.07.17 |
---|---|
드 모르간(De-Morgan)의 법칙이란? (8) | 2023.07.14 |
빅-오(Big-O) 표기법 (26) | 2023.06.12 |
우매함의 봉우리. 더닝 크루거 효과(Dunning-Kruger Effect) (2) | 2023.05.23 |
Ubuntu 프로세스 실행 시 nohup과 &에 대해서 (26) | 2023.05.12 |
댓글