diff --git a/go.mod b/go.mod index 0b327ff..8ca63fa 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module main -go 1.23.12 +go 1.25.5 require github.com/stretchr/testify v1.11.1 diff --git a/homeworks/10_allocator/homework.go b/homeworks/10_allocator/homework.go new file mode 100644 index 0000000..7f7b455 --- /dev/null +++ b/homeworks/10_allocator/homework.go @@ -0,0 +1,32 @@ +package homework10 + +import "unsafe" + +func Defragment(memory []byte, pointers []unsafe.Pointer) { + if len(memory) == 0 || len(pointers) == 0 { + return + } + + base := uintptr(unsafe.Pointer(&memory[0])) + + ptrmap := make(map[int]int) + for i, v := range pointers { + ptrmap[int(uintptr(v)-base)] = i + } + + write := 0 + for read := range memory { + if i, ok := ptrmap[read]; ok { + if write != read { + memory[write] = memory[read] + } + pointers[i] = unsafe.Pointer(&memory[write]) + write++ + } + } + + // Clear tail data + for i := write; i < len(memory); i++ { + memory[i] = 0 + } +} diff --git a/homeworks/10_allocator/homework_test.go b/homeworks/10_allocator/homework_test.go index eed791b..43b0d8e 100644 --- a/homeworks/10_allocator/homework_test.go +++ b/homeworks/10_allocator/homework_test.go @@ -1,4 +1,4 @@ -package main +package homework10 import ( "reflect" @@ -10,35 +10,6 @@ import ( // go test -v homework_test.go -func Defragment(memory []byte, pointers []unsafe.Pointer) { - if len(memory) == 0 || len(pointers) == 0 { - return - } - - base := uintptr(unsafe.Pointer(&memory[0])) - - ptrmap := make(map[int]int) - for i, v := range pointers { - ptrmap[int(uintptr(v)-base)] = i - } - - write := 0 - for read := range memory { - if i, ok := ptrmap[read]; ok { - if write != read { - memory[write] = memory[read] - } - pointers[i] = unsafe.Pointer(&memory[write]) - write++ - } - } - - // Clear tail data - for i := write; i < len(memory); i++ { - memory[i] = 0 - } -} - func TestDefragmentation(t *testing.T) { var fragmentedMemory = []byte{ 0xF3, 0x00, 0x00, 0x00, diff --git a/homeworks/11_garbage_collector/homework.go b/homeworks/11_garbage_collector/homework.go new file mode 100644 index 0000000..09ba633 --- /dev/null +++ b/homeworks/11_garbage_collector/homework.go @@ -0,0 +1,41 @@ +package homework11 + +import "unsafe" + +func Trace(stacks [][]uintptr) []uintptr { + seen := map[uintptr]bool{} + result := []uintptr{} + + var dfs func(ptr uintptr) + dfs = func(ptr uintptr) { + if ptr == 0 || seen[ptr] { + return + } + seen[ptr] = true + result = append(result, ptr) + + //nolint:govet + uptr := *(*uintptr)(unsafe.Pointer(ptr)) + dfs(uptr) + } + + for _, row := range stacks { + for _, ptr := range row { + dfs(ptr) + } + } + return result + +} + +// For test purpose we want to allocate this globally +var heapObjects = []int{ + 0x00, 0x00, 0x00, 0x00, 0x00, +} + +var ( + heapPointer1 = &heapObjects[1] + heapPointer2 = &heapObjects[2] + heapPointer3 = (*int)(nil) + heapPointer4 = &heapPointer3 +) diff --git a/homeworks/11_garbage_collector/homework_test.go b/homeworks/11_garbage_collector/homework_test.go index 2812651..b5cb463 100644 --- a/homeworks/11_garbage_collector/homework_test.go +++ b/homeworks/11_garbage_collector/homework_test.go @@ -1,4 +1,4 @@ -package main +package homework11 import ( "testing" @@ -9,44 +9,6 @@ import ( // go test -v homework_test.go -func Trace(stacks [][]uintptr) []uintptr { - seen := map[uintptr]bool{} - result := []uintptr{} - - var dfs func(ptr uintptr) - dfs = func(ptr uintptr) { - if ptr == 0 || seen[ptr] { - return - } - seen[ptr] = true - result = append(result, ptr) - - //nolint:govet - uptr := *(*uintptr)(unsafe.Pointer(ptr)) - dfs(uptr) - } - - for _, row := range stacks { - for _, ptr := range row { - dfs(ptr) - } - } - return result - -} - -// For test purpose we want to allocate this globally -var heapObjects = []int{ - 0x00, 0x00, 0x00, 0x00, 0x00, -} - -var ( - heapPointer1 = &heapObjects[1] - heapPointer2 = &heapObjects[2] - heapPointer3 = (*int)(nil) - heapPointer4 = &heapPointer3 -) - func TestTrace(t *testing.T) { var stacks = [][]uintptr{ diff --git a/homeworks/1_types/homework.go b/homeworks/1_types/homework.go new file mode 100644 index 0000000..4c6c767 --- /dev/null +++ b/homeworks/1_types/homework.go @@ -0,0 +1,31 @@ +package homework1 + +type Number interface { + ~uint16 | ~uint32 | ~uint64 +} + +func ToLittleEndian[T Number](number T) T { + switch v := any(number).(type) { + case uint16: + return T( + (v & 0x00FF << 8) | (v & 0xFF00 >> 8), + ) + case uint32: + return T( + (v & 0x000000FF << 24) | (v & 0x0000FF00 << 8) | (v & 0x00FF0000 >> 8) | (v & 0xFF000000 >> 24), + ) + case uint64: + return T( + (v & 0x00000000000000FF << 56) | + (v & 0x000000000000FF00 << 40) | + (v & 0x0000000000FF0000 << 24) | + (v & 0x00000000FF000000 << 8) | + (v & 0x000000FF00000000 >> 8) | + (v & 0x0000FF0000000000 >> 24) | + (v & 0x00FF000000000000 >> 40) | + (v & 0xFF00000000000000 >> 56), + ) + default: + panic("unsupported type") + } +} diff --git a/homeworks/1_types/homework_test.go b/homeworks/1_types/homework_test.go index f04b162..064e5d6 100644 --- a/homeworks/1_types/homework_test.go +++ b/homeworks/1_types/homework_test.go @@ -1,4 +1,4 @@ -package main +package homework1 import ( "testing" @@ -8,36 +8,6 @@ import ( // go test -v homework_test.go -type Number interface { - ~uint16 | ~uint32 | ~uint64 -} - -func ToLittleEndian[T Number](number T) T { - switch v := any(number).(type) { - case uint16: - return T( - (v & 0x00FF << 8) | (v & 0xFF00 >> 8), - ) - case uint32: - return T( - (v & 0x000000FF << 24) | (v & 0x0000FF00 << 8) | (v & 0x00FF0000 >> 8) | (v & 0xFF000000 >> 24), - ) - case uint64: - return T( - (v & 0x00000000000000FF << 56) | - (v & 0x000000000000FF00 << 40) | - (v & 0x0000000000FF0000 << 24) | - (v & 0x00000000FF000000 << 8) | - (v & 0x000000FF00000000 >> 8) | - (v & 0x0000FF0000000000 >> 24) | - (v & 0x00FF000000000000 >> 40) | - (v & 0xFF00000000000000 >> 56), - ) - default: - panic("unsupported type") - } -} - func TestConversion16(t *testing.T) { tests := map[string]struct { number uint16 diff --git a/homeworks/2_slices_and_arrays/homework.go b/homeworks/2_slices_and_arrays/homework.go new file mode 100644 index 0000000..3b8dfee --- /dev/null +++ b/homeworks/2_slices_and_arrays/homework.go @@ -0,0 +1,76 @@ +package homework2 + +type Number interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 +} + +type CircularQueue[T Number] struct { + values []T + size int + count int + front int + rear int +} + +func NewCircularQueue[T Number](size int) *CircularQueue[T] { + if size <= 0 { + panic("size must be > 0") + } + + return &(CircularQueue[T]{ + values: make([]T, size), + size: size, + rear: -1, + }) +} + +func (q *CircularQueue[T]) Push(value T) bool { + if q.Full() { + return false + } + + // Increase rear + q.rear = (q.rear + 1) % q.size + + // Set value + q.values[q.rear] = value + + q.count++ + return true +} + +func (q *CircularQueue[T]) Pop() bool { + if q.Empty() { + return false + } + + // Take value - not implemented + + // Increase front + q.front = (q.front + 1) % q.size + + q.count-- + return true +} + +func (q *CircularQueue[T]) Front() T { + if !q.Empty() { + return q.values[q.front] + } + return T(-1) +} + +func (q *CircularQueue[T]) Back() T { + if !q.Empty() { + return q.values[q.rear] + } + return T(-1) +} + +func (q *CircularQueue[T]) Empty() bool { + return q.count == 0 +} + +func (q *CircularQueue[T]) Full() bool { + return q.count == q.size +} diff --git a/homeworks/2_slices_and_arrays/homework_test.go b/homeworks/2_slices_and_arrays/homework_test.go index 6eed37c..9ff2608 100644 --- a/homeworks/2_slices_and_arrays/homework_test.go +++ b/homeworks/2_slices_and_arrays/homework_test.go @@ -1,4 +1,4 @@ -package main +package homework2 import ( "reflect" @@ -9,81 +9,6 @@ import ( // go test -v homework_test.go -type Number interface { - ~int | ~int8 | ~int16 | ~int32 | ~int64 -} - -type CircularQueue[T Number] struct { - values []T - size int - count int - front int - rear int -} - -func NewCircularQueue[T Number](size int) *CircularQueue[T] { - if size <= 0 { - panic("size must be > 0") - } - - return &(CircularQueue[T]{ - values: make([]T, size), - size: size, - rear: -1, - }) -} - -func (q *CircularQueue[T]) Push(value T) bool { - if q.Full() { - return false - } - - // Increase rear - q.rear = (q.rear + 1) % q.size - - // Set value - q.values[q.rear] = value - - q.count++ - return true -} - -func (q *CircularQueue[T]) Pop() bool { - if q.Empty() { - return false - } - - // Take value - not implemented - - // Increase front - q.front = (q.front + 1) % q.size - - q.count-- - return true -} - -func (q *CircularQueue[T]) Front() T { - if !q.Empty() { - return q.values[q.front] - } - return T(-1) -} - -func (q *CircularQueue[T]) Back() T { - if !q.Empty() { - return q.values[q.rear] - } - return T(-1) -} - -func (q *CircularQueue[T]) Empty() bool { - return q.count == 0 -} - -func (q *CircularQueue[T]) Full() bool { - return q.count == q.size -} - func TestCircularQueue(t *testing.T) { const queueSize = 3 queue := NewCircularQueue[int](queueSize) diff --git a/homeworks/3_strings/homework.go b/homeworks/3_strings/homework.go new file mode 100644 index 0000000..3b30896 --- /dev/null +++ b/homeworks/3_strings/homework.go @@ -0,0 +1,57 @@ +package homework3 + +import ( + "runtime" + "unsafe" +) + +type COWBuffer struct { + data []byte + refs *int +} + +func NewCOWBuffer(data []byte) *COWBuffer { + ref := 1 + buff := &COWBuffer{ + data: data, + refs: &ref, + } + runtime.SetFinalizer(buff, (*COWBuffer).Close) + return buff +} + +func (b *COWBuffer) Clone() COWBuffer { + *b.refs++ + return COWBuffer{ + data: b.data, + refs: b.refs, + } +} + +func (b *COWBuffer) Close() { + runtime.SetFinalizer(b, nil) + *b.refs-- +} + +func (b *COWBuffer) Update(index int, value byte) bool { + if (index >= int(len(b.data))) || (index < 0) { + return false + } + + if *b.refs > 1 { + newData := make([]byte, len(b.data)) + copy(newData, b.data) + newBuff := NewCOWBuffer(newData) + *b.refs-- + b.data = newBuff.data + b.refs = newBuff.refs + } + + b.data[index] = value + + return true +} + +func (b *COWBuffer) String() string { + return unsafe.String(&b.data[0], len(b.data)) +} diff --git a/homeworks/3_strings/homework_test.go b/homeworks/3_strings/homework_test.go index 550ad39..96d654f 100644 --- a/homeworks/3_strings/homework_test.go +++ b/homeworks/3_strings/homework_test.go @@ -1,4 +1,4 @@ -package main +package homework3 import ( "reflect" @@ -11,57 +11,6 @@ import ( // go test -v homework_test.go -type COWBuffer struct { - data []byte - refs *int -} - -func NewCOWBuffer(data []byte) *COWBuffer { - ref := 1 - buff := &COWBuffer{ - data: data, - refs: &ref, - } - runtime.SetFinalizer(buff, (*COWBuffer).Close) - return buff -} - -func (b *COWBuffer) Clone() COWBuffer { - *b.refs++ - return COWBuffer{ - data: b.data, - refs: b.refs, - } -} - -func (b *COWBuffer) Close() { - runtime.SetFinalizer(b, nil) - *b.refs-- -} - -func (b *COWBuffer) Update(index int, value byte) bool { - if (index >= int(len(b.data))) || (index < 0) { - return false - } - - if *b.refs > 1 { - newData := make([]byte, len(b.data)) - copy(newData, b.data) - newBuff := NewCOWBuffer(newData) - *b.refs-- - b.data = newBuff.data - b.refs = newBuff.refs - } - - b.data[index] = value - - return true -} - -func (b *COWBuffer) String() string { - return unsafe.String(&b.data[0], len(b.data)) -} - func TestCOWBuffer(t *testing.T) { data := []byte{'a', 'b', 'c', 'd'} buffer := NewCOWBuffer(data) diff --git a/homeworks/4_maps/maps/README.md b/homeworks/4_maps/README.md similarity index 100% rename from homeworks/4_maps/maps/README.md rename to homeworks/4_maps/README.md diff --git a/homeworks/4_maps/maps/homework_test.go b/homeworks/4_maps/homework.go similarity index 65% rename from homeworks/4_maps/maps/homework_test.go rename to homeworks/4_maps/homework.go index c7e4707..6f5abd6 100644 --- a/homeworks/4_maps/maps/homework_test.go +++ b/homeworks/4_maps/homework.go @@ -1,14 +1,6 @@ -package main +package homework4 -import ( - "cmp" - "reflect" - "testing" - - "github.com/stretchr/testify/assert" -) - -// go test -v homework_test.go +import "cmp" type OrderedMap[T cmp.Ordered] struct { count int @@ -138,48 +130,3 @@ func (m *OrderedMap[T]) ForEach(action func(key T, value any)) { } } } - -func TestCircularQueue(t *testing.T) { - data := NewOrderedMap[int]() - assert.Zero(t, data.Size()) - - data.Insert(10, 10) - data.Insert(5, 5) - data.Insert(15, 15) - data.Insert(2, 2) - data.Insert(4, 4) - data.Insert(12, 12) - data.Insert(14, 14) - - assert.Equal(t, 7, data.Size()) - assert.True(t, data.Contains(4)) - assert.True(t, data.Contains(12)) - assert.False(t, data.Contains(3)) - assert.False(t, data.Contains(13)) - - var keys []int - expectedKeys := []int{2, 4, 5, 10, 12, 14, 15} - data.ForEach(func(key int, _ any) { - keys = append(keys, key) - }) - - assert.True(t, reflect.DeepEqual(expectedKeys, keys)) - - data.Erase(15) - data.Erase(14) - data.Erase(2) - - assert.Equal(t, 4, data.Size()) - assert.True(t, data.Contains(4)) - assert.True(t, data.Contains(12)) - assert.False(t, data.Contains(2)) - assert.False(t, data.Contains(14)) - - keys = nil - expectedKeys = []int{4, 5, 10, 12} - data.ForEach(func(key int, _ any) { - keys = append(keys, key) - }) - - assert.True(t, reflect.DeepEqual(expectedKeys, keys)) -} diff --git a/homeworks/4_maps/homework_test.go b/homeworks/4_maps/homework_test.go new file mode 100644 index 0000000..8c1b4ad --- /dev/null +++ b/homeworks/4_maps/homework_test.go @@ -0,0 +1,55 @@ +package homework4 + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/assert" +) + +// go test -v homework_test.go + +func TestCircularQueue(t *testing.T) { + data := NewOrderedMap[int]() + assert.Zero(t, data.Size()) + + data.Insert(10, 10) + data.Insert(5, 5) + data.Insert(15, 15) + data.Insert(2, 2) + data.Insert(4, 4) + data.Insert(12, 12) + data.Insert(14, 14) + + assert.Equal(t, 7, data.Size()) + assert.True(t, data.Contains(4)) + assert.True(t, data.Contains(12)) + assert.False(t, data.Contains(3)) + assert.False(t, data.Contains(13)) + + var keys []int + expectedKeys := []int{2, 4, 5, 10, 12, 14, 15} + data.ForEach(func(key int, _ any) { + keys = append(keys, key) + }) + + assert.True(t, reflect.DeepEqual(expectedKeys, keys)) + + data.Erase(15) + data.Erase(14) + data.Erase(2) + + assert.Equal(t, 4, data.Size()) + assert.True(t, data.Contains(4)) + assert.True(t, data.Contains(12)) + assert.False(t, data.Contains(2)) + assert.False(t, data.Contains(14)) + + keys = nil + expectedKeys = []int{4, 5, 10, 12} + data.ForEach(func(key int, _ any) { + keys = append(keys, key) + }) + + assert.True(t, reflect.DeepEqual(expectedKeys, keys)) +} diff --git a/homeworks/4_maps/maps/main.go b/homeworks/4_maps/maps/main.go deleted file mode 100644 index cc7f060..0000000 --- a/homeworks/4_maps/maps/main.go +++ /dev/null @@ -1,54 +0,0 @@ -package main - -import ( - "fmt" - "unsafe" -) - -type hmap struct { - count int - flags uint8 - B uint8 - noverflow uint16 - hash0 uint32 - buckets unsafe.Pointer - oldbuckets unsafe.Pointer - nevacuate uintptr - extra unsafe.Pointer -} - -func main() { - - m := make(map[int]int, 8) - - m[0] = 0 - m[1] = 1 - m[2] = 2 - m[3] = 3 - m[4] = 4 - m[5] = 5 - m[6] = 6 - m[7] = 7 - m[8] = 8 - m[9] = 9 - m[10] = 10 - m[11] = 11 - m[12] = 12 - // m[13] = 13 - // m[14] = 14 - // m[15] = 15 - // m[16] = 16 - // m[17] = 17 - // m[18] = 18 - // m[19] = 19 - - clear(m) - // delete(m, 0) - - h := *(*hmap)(*(*unsafe.Pointer)(unsafe.Pointer(&m))) - - fmt.Printf("count=%d\n", h.count) - fmt.Printf("B=%d\n", h.B) - fmt.Printf("flags=%08b\n", h.flags) - fmt.Printf("hash0=%d\n", h.hash0) -} diff --git a/homeworks/5_functions/homework.go b/homeworks/5_functions/homework.go new file mode 100644 index 0000000..37d8b4b --- /dev/null +++ b/homeworks/5_functions/homework.go @@ -0,0 +1,35 @@ +package homework5 + +func Map[T any](data []T, action func(T) T) []T { + if data == nil { + return nil + } + res := make([]T, len(data)) + for i := range data { + res[i] = action(data[i]) + } + return res +} + +func Filter[T any](data []T, action func(T) bool) []T { + if data == nil { + return nil + } + res := []T{} + for i := range data { + if action(data[i]) { + res = append(res, data[i]) + } + } + return res +} + +func Reduce[T any](data []T, initial T, action func(T, T) T) T { + if data == nil { + return initial + } + for _, v := range data { + initial = action(v, initial) + } + return initial +} diff --git a/homeworks/5_functions/homework_test.go b/homeworks/5_functions/homework_test.go index f552d70..eb5407b 100644 --- a/homeworks/5_functions/homework_test.go +++ b/homeworks/5_functions/homework_test.go @@ -1,4 +1,4 @@ -package main +package homework5 import ( "reflect" @@ -9,40 +9,6 @@ import ( // go test -v homework_test.go -func Map[T any](data []T, action func(T) T) []T { - if data == nil { - return nil - } - res := make([]T, len(data)) - for i := range data { - res[i] = action(data[i]) - } - return res -} - -func Filter[T any](data []T, action func(T) bool) []T { - if data == nil { - return nil - } - res := []T{} - for i := range data { - if action(data[i]) { - res = append(res, data[i]) - } - } - return res -} - -func Reduce[T any](data []T, initial T, action func(T, T) T) T { - if data == nil { - return initial - } - for _, v := range data { - initial = action(v, initial) - } - return initial -} - func TestMap(t *testing.T) { tests := map[string]struct { data []int diff --git a/homeworks/6_struct/homework.go b/homeworks/6_struct/homework.go new file mode 100644 index 0000000..535e335 --- /dev/null +++ b/homeworks/6_struct/homework.go @@ -0,0 +1,241 @@ +package homework6 + +type Option func(*GamePerson) + +func WithName(name string) func(*GamePerson) { + return func(person *GamePerson) { + copy(person.name[:], name) + } +} + +func WithCoordinates(x, y, z int) func(*GamePerson) { + return func(person *GamePerson) { + person.x = int32(x) + person.y = int32(y) + person.z = int32(z) + } +} + +func WithGold(gold int) func(*GamePerson) { + return func(person *GamePerson) { + person.gold = uint32(gold) + } +} + +func WithMana(mana int) func(*GamePerson) { + return func(person *GamePerson) { + person.params = paramsLayout.mana.set(person.params, mana) + } +} + +func WithHealth(health int) func(*GamePerson) { + return func(person *GamePerson) { + person.params = paramsLayout.health.set(person.params, health) + } +} + +func WithRespect(respect int) func(*GamePerson) { + return func(person *GamePerson) { + person.params = paramsLayout.respect.set(person.params, respect) + } +} + +func WithStrength(strength int) func(*GamePerson) { + return func(person *GamePerson) { + person.params = paramsLayout.strength.set(person.params, strength) + } +} + +func WithExperience(experience int) func(*GamePerson) { + return func(person *GamePerson) { + person.expLevel = expLevelLayout.experience.set(person.expLevel, experience) + } +} + +func WithLevel(level int) func(*GamePerson) { + return func(person *GamePerson) { + person.expLevel = expLevelLayout.level.set(person.expLevel, level) + } +} + +func WithHouse() func(*GamePerson) { + return func(person *GamePerson) { + person.setFlag(house) + } +} + +func WithGun() func(*GamePerson) { + return func(person *GamePerson) { + person.setFlag(house) + } +} + +func WithFamily() func(*GamePerson) { + return func(person *GamePerson) { + person.setFlag(family) + } +} + +func WithType(personType int) func(*GamePerson) { + return func(person *GamePerson) { + person.params = paramsLayout.personType.set(person.params, personType) + } +} + +const ( + BuilderGamePersonType = iota + BlacksmithGamePersonType + WarriorGamePersonType +) + +const ( + usernameLength = 42 + + // Flags + house = 1 + gun = 2 + family = 4 +) + +// 64 byte +type GamePerson struct { + x int32 // 4 byte + y int32 // 4 byte + z int32 // 4 byte + gold uint32 // 4 byte + params uint32 // 4 byte + flags uint8 // 1 byte + expLevel uint8 // 1 byte + name [usernameLength]byte // 1*42 byte +} + +type bitField32 = bitField[uint32] +type bitField8 = bitField[uint8] + +var paramsLayout = struct { + mana bitField32 + health bitField32 + respect bitField32 + strength bitField32 + personType bitField32 +}{ + mana: newBitField32(0, 10), + health: newBitField32(10, 10), + respect: newBitField32(20, 4), + strength: newBitField32(24, 4), + personType: newBitField32(28, 4), +} + +var expLevelLayout = struct { + experience bitField8 + level bitField8 +}{ + experience: newBitField8(0, 4), + level: newBitField8(4, 4), +} + +type bitField[T uint32 | uint8] struct { + offset uint8 + size uint8 + mask T +} + +func newBitField32(offset, size uint8) bitField32 { + return newBitField[uint32](offset, size) +} + +func newBitField8(offset, size uint8) bitField8 { + return newBitField[uint8](offset, size) +} + +func newBitField[T uint8 | uint32](offset, size uint8) bitField[T] { + return bitField[T]{ + offset: offset, + size: size, + mask: (1 << size) - 1, + } +} + +func (bf *bitField[T]) get(source T) int { + return int((source >> bf.offset) & bf.mask) +} + +func (bf *bitField[T]) set(source T, value int) T { + return (source & ^(bf.mask << bf.offset)) | (T(value)&bf.mask)<> bf.offset) & bf.mask) -} - -func (bf *bitField[T]) set(source T, value int) T { - return (source & ^(bf.mask << bf.offset)) | (T(value)&bf.mask)< 0 { + sb.WriteByte('\n') + } + // sb.WriteString(fieldType.Name) + sb.WriteString(parsedTag.name) + sb.WriteByte('=') + fmt.Fprint(&sb, value) + } + + return sb.String() +} + +func parseTag(tag string) ParsedTag { + parsedTag := ParsedTag{} + parts := strings.Split(tag, ",") + + for _, v := range parts { + switch v { + case emptyTag: + parsedTag.omitempty = true + default: + parsedTag.name = v + } + + } + + return parsedTag +} diff --git a/homeworks/9_generics_and_reflection/homework_test.go b/homeworks/9_generics_and_reflection/homework_test.go index c44f52e..fcba082 100644 --- a/homeworks/9_generics_and_reflection/homework_test.go +++ b/homeworks/9_generics_and_reflection/homework_test.go @@ -1,9 +1,6 @@ -package main +package homework9 import ( - "fmt" - "reflect" - "strings" "testing" "github.com/stretchr/testify/assert" @@ -11,76 +8,6 @@ import ( // go test -v homework_test.go -type Person struct { - Name string `properties:"name"` - Address string `properties:"address,omitempty"` - Age int `properties:"age"` - Married bool `properties:"married"` -} - -type ParsedTag struct { - name string - omitempty bool -} - -const ( - propertiesTag = "properties" - emptyTag = "omitempty" -) - -func Serialize(object any) string { - var sb strings.Builder - t := reflect.TypeOf(object) - v := reflect.ValueOf(object) - for i := 0; i < v.NumField(); i++ { - fieldType := t.Field(i) - fieldValue := v.Field(i) - - tag, ok := fieldType.Tag.Lookup(propertiesTag) - if !ok { - continue - } - - parsedTag := parseTag(tag) - - if fieldValue.IsZero() && parsedTag.omitempty { - continue - } - - if !fieldValue.CanInterface() { - continue - } - value := fieldValue.Interface() - - if i > 0 { - sb.WriteByte('\n') - } - // sb.WriteString(fieldType.Name) - sb.WriteString(parsedTag.name) - sb.WriteByte('=') - fmt.Fprint(&sb, value) - } - - return sb.String() -} - -func parseTag(tag string) ParsedTag { - parsedTag := ParsedTag{} - parts := strings.Split(tag, ",") - - for _, v := range parts { - switch v { - case emptyTag: - parsedTag.omitempty = true - default: - parsedTag.name = v - } - - } - - return parsedTag -} - func TestSerialization(t *testing.T) { tests := map[string]struct { person Person