/**
* DeukPackCodec — 직렬화 헬퍼. DpProtocolLibrary 모듈화.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
namespace DeukPack.Protocol
{
///
/// DeukPack serialization helper - modularized to reduce code duplication
/// Recursive serialization support for nested structures
///
public static class DeukPackCodec
{
/// 내부 을 둔 바이너리 팩 프로토콜 (쓰기 후 ).
public static DpBinaryProtocol OpenBinaryPack()
=> new DpBinaryProtocol();
/// 바이너리 와이어 — 스트림에 팩(쓰기).
public static DpBinaryProtocol OpenBinaryPack(Stream stream, bool bigEndian = true, bool strictRead = true, bool strictWrite = true, int initialBufferSize = 4096)
=> new DpBinaryProtocol(stream, bigEndian, strictRead, strictWrite, initialBufferSize);
/// 바이너리 와이어 — 스트림에서 언팩(읽기). 타입은 과 동일.
public static DpBinaryProtocol OpenBinaryUnpack(Stream stream, bool bigEndian = true, bool strictRead = true, bool strictWrite = true, int initialBufferSize = 4096)
=> new DpBinaryProtocol(stream, bigEndian, strictRead, strictWrite, initialBufferSize);
/// 객체 전체를 byte[] 로 팩. 와 동일.
public static byte[] Pack(IDpSerializable obj, DpFormat format = DpFormat.Binary, bool pretty = false)
=> Serialize(obj, format, pretty);
/// byte[] 를 객체로 언팩. 와 동일.
public static T Unpack(byte[] data, DpFormat format = DpFormat.Binary) where T : IDpSerializable, new()
=> Deserialize(data, format);
/// byte[] 를 기준 객체에 덮어쓰기(Zero-Alloc 언팩). 와 동일.
public static void Unpack(IDpSerializable obj, byte[] data, DpFormat format = DpFormat.Binary)
=> DeserializeInto(obj, data, format);
/// 사용자 정의 Write 액션을 이용해 팩 (오버라이드용).
public static byte[] PackAction(Action writeAction, DpFormat format = DpFormat.Binary, bool pretty = false)
{
var ms = new System.IO.MemoryStream();
DpProtocol p = CreateProtocol(ms, format, pretty);
writeAction(p);
return ms.ToArray();
}
[Obsolete("Use Unpack(obj, data, format) instead.", false)]
public static void UnpackInto(IDpSerializable obj, byte[] data, DpFormat format = DpFormat.Binary)
=> Unpack(obj, data, format);
///
/// Write a value recursively based on its type
///
public static void WriteValue(DpProtocol oprot, DpWireType type, object? value)
{
if (value == null)
return;
switch (type)
{
case DpWireType.Bool:
oprot.WriteBool((bool)value);
break;
case DpWireType.Byte:
oprot.WriteByte(value is sbyte sb ? unchecked((byte)sb) : Convert.ToByte(value));
break;
case DpWireType.Int16:
oprot.WriteI16(value is short s16 ? s16 : Convert.ToInt16(value));
break;
case DpWireType.Int32: // U32 동일 와이어(§2.2.1)
if (value is int i32) oprot.WriteI32(i32);
else if (value is uint u32) oprot.WriteI32(unchecked((int)u32));
else oprot.WriteI32(Convert.ToInt32(value));
break;
case DpWireType.Int64: // U64 동일 와이어
if (value is long i64) oprot.WriteI64(i64);
else if (value is ulong u64) oprot.WriteI64(unchecked((long)u64));
else oprot.WriteI64(Convert.ToInt64(value));
break;
case DpWireType.Double:
oprot.WriteDouble(Convert.ToDouble(value));
break;
case DpWireType.String:
if (value is byte[] bytes)
oprot.WriteBinary(bytes);
else
oprot.WriteString(value as string);
break;
case DpWireType.Struct:
if (value is IDpSerializable serializable)
serializable.Write(oprot);
break;
default:
if (value is IDpSerializable serializableValue)
serializableValue.Write(oprot);
break;
}
}
///
/// Read a value recursively based on its type
///
public static object? ReadValue(DpProtocol iprot, DpWireType type, Type? targetType = null)
{
switch (type)
{
case DpWireType.Bool:
return iprot.ReadBool();
case DpWireType.Byte:
{
byte v = iprot.ReadByte();
if (targetType == typeof(sbyte)) return unchecked((sbyte)v);
return v;
}
case DpWireType.Int16:
{
short v = iprot.ReadI16();
return (targetType != null && targetType.IsEnum) ? Enum.ToObject(targetType, v) : (object)v;
}
case DpWireType.Int32:
{
int v = iprot.ReadI32();
if (targetType != null && targetType.IsEnum) return Enum.ToObject(targetType, v);
if (targetType == typeof(uint)) return unchecked((uint)v);
return v;
}
case DpWireType.Int64:
{
long v = iprot.ReadI64();
if (targetType != null && targetType.IsEnum) return Enum.ToObject(targetType, v);
if (targetType == typeof(ulong)) return unchecked((ulong)v);
return v;
}
case DpWireType.Double:
{
double v = iprot.ReadDouble();
if (targetType == typeof(float)) return (float)v;
return v;
}
case DpWireType.String:
if (targetType == typeof(string))
return iprot.ReadString();
return iprot.ReadBinary();
case DpWireType.Struct:
if (targetType != null)
{
var instance = Activator.CreateInstance(targetType);
if (instance is IDpSerializable serializable)
{
serializable.Read(iprot);
return instance;
}
}
return null;
default:
if (targetType != null)
{
var instance = Activator.CreateInstance(targetType);
if (instance is IDpSerializable serializable)
{
serializable.Read(iprot);
return instance;
}
}
return null;
}
}
///
/// Write a list recursively
///
public static void WriteList(DpProtocol oprot, DpWireType elementType, IEnumerable? list)
{
if (list == null) return;
var count = list is ICollection collection ? collection.Count : list.Count();
oprot.WriteListBegin(new DpList { ElementType = elementType, Count = count });
foreach (var item in list)
{
WriteValue(oprot, elementType, item);
}
oprot.WriteListEnd();
}
///
/// Read a list recursively
///
public static List ReadList(DpProtocol iprot, DpWireType elementType, Func? reader = null)
{
var listInfo = iprot.ReadListBegin();
var list = new List(listInfo.Count);
for (int i = 0; i < listInfo.Count; i++)
{
if (reader != null)
{
list.Add(reader(iprot));
}
else
{
var value = ReadValue(iprot, elementType, typeof(T));
if (value is T item)
{
list.Add(item);
}
}
}
iprot.ReadListEnd();
return list;
}
///
/// 기존 인스턴스에 덮어쓰기 역직렬화 (Zero-Alloc In-place Unpack).
///
/// 와 달리 새 인스턴스를 생성하지 않습니다.
/// In-place Unpack(Hero.Unpack(cached, data)) 패턴에서 list 필드 GC를 제거합니다.
///
///
/// 주의: 이 null이면 동작하지 않습니다.
/// 호출 전 this.Items ??= new List<T>(); 로 초기화하십시오.
///
///
public static void ReadListInto(
DpProtocol iprot,
DpWireType elementType,
List target,
Func? reader = null)
{
if (target == null) throw new ArgumentNullException(nameof(target));
var listInfo = iprot.ReadListBegin();
target.Clear(); // 기존 인스턴스 재사용
if (target.Capacity < listInfo.Count)
target.Capacity = listInfo.Count; // resize 최소화 (1회만)
for (int i = 0; i < listInfo.Count; i++)
{
if (reader != null)
{
target.Add(reader(iprot));
}
else
{
var value = ReadValue(iprot, elementType, typeof(T));
if (value is T item) target.Add(item);
}
}
iprot.ReadListEnd();
}
///
/// List<T>를 Deep Clone합니다 (LINQ Enumerator GC 없이 Capacity-aware foreach).
/// struct 원소를 포함한 list 필드의 Clone() 생성 코드에서 사용됩니다.
///
public static List CloneList(List? source, Func cloneElem)
{
if (source == null) return new List();
var result = new List(source.Count);
foreach (var item in source) result.Add(cloneElem(item));
return result;
}
/// HashSet<T>를 Deep Clone합니다.
public static HashSet CloneSet(HashSet? source, Func cloneElem)
{
if (source == null) return new HashSet();
var result = new HashSet(); // netstandard2.0 compatible
foreach (var item in source) result.Add(cloneElem(item));
return result;
}
/// Dictionary<TKey,TValue>를 Deep Clone합니다.
public static Dictionary CloneMap(
Dictionary? source,
Func, TKey> keySelector,
Func, TValue> valueSelector)
where TKey : notnull
{
if (source == null) return new Dictionary();
var result = new Dictionary(source.Count);
foreach (var kvp in source) result[keySelector(kvp)] = valueSelector(kvp);
return result;
}
///
/// Write a set recursively
///
public static void WriteSet(DpProtocol oprot, DpWireType elementType, IEnumerable set)
{
var count = set is ICollection collection ? collection.Count : set.Count();
oprot.WriteSetBegin(new DpSet { ElementType = elementType, Count = count });
foreach (var item in set)
{
WriteValue(oprot, elementType, item);
}
oprot.WriteSetEnd();
}
///
/// Read a set recursively
///
public static HashSet ReadSet(DpProtocol iprot, DpWireType elementType, Func? reader = null)
{
var setInfo = iprot.ReadSetBegin();
var set = new HashSet();
for (int i = 0; i < setInfo.Count; i++)
{
if (reader != null)
{
set.Add(reader(iprot));
}
else
{
var value = ReadValue(iprot, elementType, typeof(T));
if (value is T item)
{
set.Add(item);
}
}
}
iprot.ReadSetEnd();
return set;
}
/// 기존 인스턴스에 덮어쓰기 역직렬화.
public static void ReadSetInto(DpProtocol iprot, DpWireType elementType, HashSet target, Func? reader = null)
{
if (target == null) throw new ArgumentNullException(nameof(target));
var setInfo = iprot.ReadSetBegin();
target.Clear();
for (int i = 0; i < setInfo.Count; i++)
{
if (reader != null) target.Add(reader(iprot));
else
{
var value = ReadValue(iprot, elementType, typeof(T));
if (value is T item) target.Add(item);
}
}
iprot.ReadSetEnd();
}
///
/// Write a map recursively
///
public static void WriteMap(DpProtocol oprot, DpWireType keyType, DpWireType valueType, IDictionary map)
{
oprot.WriteMapBegin(new DpDict { KeyType = keyType, ValueType = valueType, Count = map.Count });
foreach (var kvp in map)
{
WriteValue(oprot, keyType, kvp.Key);
WriteValue(oprot, valueType, kvp.Value);
}
oprot.WriteMapEnd();
}
///
/// Read a map recursively
///
public static Dictionary ReadMap(
DpProtocol iprot,
DpWireType keyType,
DpWireType valueType,
Func? keyReader = null,
Func? valueReader = null)
where TKey : notnull
{
var mapInfo = iprot.ReadMapBegin();
var map = new Dictionary(mapInfo.Count);
for (int i = 0; i < mapInfo.Count; i++)
{
TKey key;
if (keyReader != null)
{
key = keyReader(iprot);
}
else
{
var keyValue = ReadValue(iprot, keyType, typeof(TKey));
key = keyValue is TKey k ? k : default!;
}
TValue value;
if (valueReader != null)
{
value = valueReader(iprot);
}
else
{
var valueObj = ReadValue(iprot, valueType, typeof(TValue));
value = valueObj is TValue v ? v : default!;
}
map[key!] = value;
}
iprot.ReadMapEnd();
return map;
}
/// 기존 인스턴스에 덮어쓰기 역직렬화.
public static void ReadMapInto(
DpProtocol iprot,
DpWireType keyType,
DpWireType valueType,
Dictionary target,
Func? keyReader = null,
Func? valueReader = null)
where TKey : notnull
{
if (target == null) throw new ArgumentNullException(nameof(target));
var mapInfo = iprot.ReadMapBegin();
target.Clear();
for (int i = 0; i < mapInfo.Count; i++)
{
TKey key = (keyReader != null) ? keyReader(iprot) : (TKey)ReadValue(iprot, keyType, typeof(TKey))!;
TValue value = (valueReader != null) ? valueReader(iprot) : (TValue)ReadValue(iprot, valueType, typeof(TValue))!;
target[key] = value;
}
iprot.ReadMapEnd();
}
/// 득팩 객체 → byte[]. 는 JSON/Deuk JSON/Deuk YAML 에만 적용(바이너리는 무시).
public static byte[] Serialize(IDpSerializable obj, DpFormat format = DpFormat.Binary, bool pretty = false)
{
var ms = new System.IO.MemoryStream();
DpProtocol p = CreateProtocol(ms, format, pretty);
obj.Write(p);
return ms.ToArray();
}
/// byte[] → 득팩 객체. 은 저장 시 사용한 프로토콜과 같아야 한다(자동 판별 없음).
public static T Deserialize(byte[] data, DpFormat format = DpFormat.Binary) where T : IDpSerializable, new()
{
var ms = new System.IO.MemoryStream(data);
DpProtocol p = CreateProtocol(ms, format, false);
var obj = new T();
obj.Read(p);
return obj;
}
/// byte[] 를 기존 객체에 덮어쓰기. 가비지 생성 없이(Zero-Alloc) 상태를 갱신합니다.
public static void DeserializeInto(IDpSerializable obj, byte[] data, DpFormat format = DpFormat.Binary)
{
var ms = new System.IO.MemoryStream(data);
DpProtocol p = CreateProtocol(ms, format, false);
obj.Read(p);
}
/// 객체를 JSON 문자열로 덤프 (디버깅/덤프용)
public static string ToString(IDpSerializable obj, bool pretty = true)
{
var bytes = Serialize(obj, DpFormat.Json, pretty);
return System.Text.Encoding.UTF8.GetString(bytes);
}
private static DpProtocol CreateProtocol(System.IO.Stream stream, DpFormat format, bool pretty)
{
switch (format)
{
case DpFormat.Json:
return new DpJsonProtocol(stream, pretty);
case DpFormat.DeukJson:
return new DpDeukJsonProtocol(stream, pretty);
case DpFormat.DeukYaml:
return new DpDeukYamlProtocol(stream, pretty);
case DpFormat.Binary:
default:
return OpenBinaryPack(stream);
}
}
}
///
/// 득팩 통합 API 파사드. 모든 타겟 언어에서 네임스페이스 식별자를 통일하기 위해 제공됩니다.
/// Usage: deukPack.Serialize(...) / deukPack.DeserializeInto(...)
///
public static class deukPack
{
/// 객체를 バイナリ(Binary) 바이트 배열로 직렬화 (Pack)
public static byte[] Pack(IDpSerializable obj)
=> DeukPackCodec.Serialize(obj, DpFormat.Binary, false);
/// 바이트 배열을 기존 객체에 덮어쓰기 역직렬화 (Unpack, Zero-Alloc)
public static void UnpackInto(IDpSerializable obj, byte[] data)
=> DeukPackCodec.DeserializeInto(obj, data, DpFormat.Binary);
/// 바이트 배열을 새 객체로 역직렬화 (Unpack)
public static T Unpack(byte[] data) where T : IDpSerializable, new()
=> DeukPackCodec.Deserialize(data, DpFormat.Binary);
/// 객체를 JSON 문자열로 직렬화
public static string ToJson(IDpSerializable obj, bool pretty = false)
=> System.Text.Encoding.UTF8.GetString(DeukPackCodec.Serialize(obj, DpFormat.Json, pretty));
/// JSON 바이트 배열 또는 텍스트 기반 바이트를 객체로 역직렬화
public static T FromJson(byte[] jsonBytes) where T : IDpSerializable, new()
=> DeukPackCodec.Deserialize(jsonBytes, DpFormat.Json);
// --- Legacy Backwards Compatibility (Deprecated) ---
[Obsolete("Use Pack() instead. This method is kept for backwards compatibility.")]
public static byte[] Serialize(IDpSerializable obj, DpFormat format = DpFormat.Binary, bool pretty = false)
=> DeukPackCodec.Serialize(obj, format, pretty);
[Obsolete("Use UnpackInto() instead. This method is kept for backwards compatibility.")]
public static void DeserializeInto(IDpSerializable obj, byte[] data, DpFormat format = DpFormat.Binary)
=> DeukPackCodec.DeserializeInto(obj, data, format);
[Obsolete("Use Unpack() instead. This method is kept for backwards compatibility.")]
public static T Deserialize(byte[] data, DpFormat format = DpFormat.Binary) where T : IDpSerializable, new()
=> DeukPackCodec.Deserialize(data, format);
}
}