Browse Source

SVI Реализация пакера

SVI 2 years ago
parent
commit
35ac7fdb07

+ 2 - 1
.gitignore

@@ -1,2 +1,3 @@
 
-bin
+bin
+.vscode

+ 3 - 1
Makefile

@@ -7,4 +7,6 @@ app.run:
 	go fmt ./...
 	go build -o ./bin/gpz ./cmd/packer/main.go
 	cd ./bin && \
-	./gpz --pack=./v_i_m.html
+	export PACKER_CMD="pack" && \
+	export PACK_FILE="./v_i_m.html" && \
+	./gpz

+ 3 - 1
cmd/packer/main.go

@@ -14,5 +14,7 @@ func main() {
 		log.Printf("main(): in create IService, err=\n\t%v\n", err)
 		return
 	}
-	serv.Run()
+	if err := serv.Run(); err != nil {
+		log.Printf("main(): in run IService, err=\n\t%v\n", err)
+	}
 }

+ 66 - 0
internal/packer/dict_keys/dict_keys.go

@@ -0,0 +1,66 @@
+// package dict_keys -- словарь ключей для подстановки
+package dict_keys
+
+import (
+	"fmt"
+	"sync"
+
+	"p78git.ddns.net/svi/packer/pkg/types"
+)
+
+// DictKeys -- словарь ключей для подстановки
+type DictKeys struct {
+	dictKey map[uint16]bool // Словарь ключей подстановки с частотой встреч (для фреквенсера)
+	block   sync.RWMutex
+}
+
+// NewDictKeys -- возвращает новый словарь для подстановки ключей
+func NewDictKeys() *DictKeys {
+	sf := &DictKeys{
+		dictKey: map[uint16]bool{},
+	}
+	_ = types.IDictKeys(sf)
+	return sf
+}
+
+// Keys -- возвращает словарь ключей
+func (sf *DictKeys) Keys() []uint16 {
+	sf.block.RLock()
+	defer sf.block.RUnlock()
+	lstKey := []uint16{}
+	for key := range sf.dictKey {
+		lstKey = append(lstKey, key)
+	}
+
+	return lstKey
+}
+
+// Add -- добавляет ключ в словарь
+func (sf *DictKeys) Add(key uint16) {
+	sf.block.Lock()
+	defer sf.block.Unlock()
+	sf.dictKey[key] = true
+}
+
+// Stat -- отдаёт статистику словаря
+func (sf *DictKeys) Stat() string {
+	sf.block.RLock()
+	defer sf.block.RUnlock()
+	res := fmt.Sprintf("DictKeys{len:%v}", len(sf.dictKey))
+	return res
+}
+
+// IsExists -- проверяет существует ли такой ключ
+func (sf *DictKeys) IsExists(key uint16) bool {
+	sf.block.RLock()
+	defer sf.block.RUnlock()
+	_, isOk := sf.dictKey[key]
+	return isOk
+}
+
+// Len -- возвращает размер словаря ключей
+func (sf *DictKeys) Len() int {
+	sf.block.RLock()
+	defer sf.block.RUnlock()
+	return len(sf.dictKey)
+}

+ 45 - 0
internal/packer/freq_pair/freq_pair.go

@@ -0,0 +1,45 @@
+// package freq_pair -- наиблоее частотная пара
+package freq_pair
+
+import (
+	"fmt"
+)
+
+// FreqPair -- самая частотная пара
+type FreqPair struct {
+	Val1_, Val2_ int    // Самые частотные значения
+	Key0_, Key1_ uint16 // Самый частотные ключи
+	Combo_       string // Самая выгодная комбинация для сжатия(11, 12, 21, 22)
+}
+
+// SetVal2 -- устанавливает значения частотных пар
+func (sf *FreqPair) SetVal1(key uint16, val int) {
+	if val > sf.Val2_ {
+		sf.Val1_ = sf.Val2_
+		sf.Key0_ = sf.Key1_
+
+		sf.Val2_ = val
+		sf.Key1_ = key
+		return
+	}
+	sf.Val1_ = val
+	sf.Key0_ = key
+}
+
+// String -- строковое представление частотной пары
+func (sf *FreqPair) String() string {
+	str := fmt.Sprintf("{key1:%v val1:%v; key2:%v val2:%v; combo:%q}",
+		sf.Key0_,
+		sf.Val1_,
+		sf.Key1_,
+		sf.Val2_,
+		sf.Combo_,
+	)
+	return str
+}
+
+// SetKeys -- устанавливает ключи замен
+func (sf *FreqPair) SetKeys(key0, key1 uint16) {
+	sf.Key0_ = key0
+	sf.Key1_ = key1
+}

+ 123 - 0
internal/packer/frequencer/frequencer.go

@@ -0,0 +1,123 @@
+// package frequencer -- ищет минимально частотную пару
+package frequencer
+
+import (
+	"fmt"
+	"log"
+	"time"
+
+	"p78git.ddns.net/svi/packer/pkg/types"
+)
+
+// Frequencer -- ищет минимально частотную пару
+type Frequencer struct {
+	serv       types.IService
+	packer     types.IPacker
+	key0, key1 uint16          // Частотные кючи
+	oldKeys    map[uint32]bool // Перебранные комбинации для key0
+}
+
+// NewFrequencer -- возвращает новый минимальный искатель
+func NewFrequencer(packer types.IPacker) (*Frequencer, error) {
+	if packer == nil {
+		return nil, fmt.Errorf("NewFrequencer(): IPacker==nil")
+	}
+	sf := &Frequencer{
+		serv:    packer.Serv(),
+		packer:  packer,
+		oldKeys: map[uint32]bool{},
+	}
+	return sf, nil
+}
+
+// Run -- запускает вычисление частотной пары
+//
+//	Считается успехом, если нашёл 5 замен словарных ключей
+func (sf *Frequencer) Run() error {
+	lstKeys := sf.serv.Packer().DictKeys().Keys()
+	if len(lstKeys) <= 1 {
+		return fmt.Errorf("Frequencer.Run(): lstKeys is empty")
+	}
+	key0 := uint16(0)
+	key1 := uint16(0)
+	binData := sf.serv.Packer().BinData()
+	num := uint32(0)
+	fnCheckPair := func() bool {
+		num = uint32(key0)*65536 + uint32(key1)
+		_, isOk := sf.oldKeys[num]
+		return isOk
+	}
+	fnAddKey := func() {
+		num = uint32(key0)*65536 + uint32(key1)
+		sf.oldKeys[num] = true
+	}
+	count := 0
+	val0 := uint16(0)
+	fnCompare := func(ind int) {
+		val1 := binData[ind]
+		if val0 != key0 {
+			return
+		}
+		if val1 != key1 {
+			return
+		}
+		count++
+		if ind >= len(binData) {
+			return
+		}
+		val0 = binData[ind]
+	}
+	fnKey1 := func() bool { // Ищет совпадение для key0 и key1
+		timeBeg := time.Now().UnixMilli() // Для ограничения времени поиска бесполезной комбинации
+		for _, key1 = range lstKeys {
+			if fnCheckPair() {
+				continue
+			}
+			count = 0
+			val0 = binData[0]
+			countCall := 0
+			for ind := 1; ind < len(binData); ind++ {
+				val1 := binData[ind]
+				fnCompare(ind)
+				countCall++
+				if countCall > 5000 {
+					timeDelta := time.Now().UnixMilli() - timeBeg
+					if timeDelta > 100 {
+						fnAddKey()
+						return false
+					}
+					countCall = 0
+				}
+
+				if count >= 10 {
+					fnAddKey()
+					timeDelta := time.Now().UnixMilli() - timeBeg
+					log.Printf("Frequencer.fnKey1(): timeDelta=%vmsec\n", timeDelta)
+					return true
+				}
+				val0 = val1
+			}
+			fnAddKey()
+		}
+		return false
+	}
+	isFind := false
+	for _, key0 = range lstKeys {
+		if fnKey1() {
+			isFind = true
+			break
+		}
+	}
+	if !isFind {
+		return fmt.Errorf("Frequencer.Run(): not find keys for change, checked=%v", len(sf.oldKeys))
+	}
+	sf.key0 = key0
+	sf.key1 = key1
+	log.Printf("Frequencer.Run(): keys=[%v %v], checked=%v\n", sf.key0, sf.key1, len(sf.oldKeys))
+	return nil
+}
+
+// Keys -- возвращает найденные ключи
+func (sf *Frequencer) Keys() (uint16, uint16) {
+	return sf.key0, sf.key1
+}

+ 166 - 2
internal/packer/packer.go

@@ -3,13 +3,28 @@ package packer
 
 import (
 	"fmt"
+	"log"
+	"math"
+	"os"
+	"time"
 
+	"p78git.ddns.net/svi/packer/internal/packer/dict_keys"
+	"p78git.ddns.net/svi/packer/internal/packer/freq_pair"
+	"p78git.ddns.net/svi/packer/internal/packer/frequencer"
 	"p78git.ddns.net/svi/packer/pkg/types"
 )
 
 // Packer -- упаковщик сервиса
 type Packer struct {
-	serv types.IService
+	serv        types.IService
+	dictKeys    types.IDictKeys     // Словарь ключей
+	freqPair    *freq_pair.FreqPair // Частотная пара
+	frequencer  types.IFrequencer   // Подбор частотной пары
+	binData     []uint16            // То, что останется после замен
+	timeTotal   float32             // Общее время в сек
+	totalChange int                 // Общее число замен
+	originSize  int                 // Оригинальный размер
+	binNew      []uint16            // Буфер новых данных
 }
 
 // NewPacker -- возвращает ноый пакер
@@ -18,7 +33,156 @@ func NewPacker(serv types.IService) (*Packer, error) {
 		return nil, fmt.Errorf("NewPacker(): IService==nil")
 	}
 	sf := &Packer{
-		serv: serv,
+		serv:     serv,
+		dictKeys: dict_keys.NewDictKeys(),
+		freqPair: &freq_pair.FreqPair{},
+		binData:  []uint16{},
+		binNew:   []uint16{0, 0, 0},
 	}
+	var err error
+	sf.frequencer, err = frequencer.NewFrequencer(sf)
+	if err != nil {
+		return nil, fmt.Errorf("NewPacker(): in create IFrequencer, err=\n\t%w", err)
+	}
+	_ = types.IPacker(sf)
 	return sf, nil
 }
+
+// Run -- запускает упаковку
+func (sf *Packer) Run() error {
+	log.Printf("Packer.Run()\n")
+	{ // Чтение файла
+		fileName := os.Getenv("PACK_FILE")
+		if fileName == "" {
+			return fmt.Errorf("Packer.Run(): env PACK_FILE not set")
+		}
+		binData, err := os.ReadFile(fileName)
+		if err != nil {
+			return fmt.Errorf("Packer.Run(): in read %q, err=\n\t%w", fileName, err)
+		}
+		sf.originSize = len(binData)
+		for _, val := range binData {
+			sf.binData = append(sf.binData, uint16(val))
+		}
+		log.Printf("Packer.Run(): len(%q)=%v\n", fileName, len(sf.binData))
+	}
+	round := 1
+	for { // Главный цикл сжатия
+		log.Printf("Packer.Run(): round=%v, lenDict=%v\n", round, sf.dictKeys.Len())
+		if !sf.round() {
+			break
+		}
+		round++
+	}
+	return nil
+}
+
+// Serv -- возвращает сервис
+func (sf *Packer) Serv() types.IService {
+	return sf.serv
+}
+
+// BinData -- возвращает бинарны данные для упаковки
+func (sf *Packer) BinData() []uint16 {
+	return sf.binData
+}
+
+// DictKeys -- возвращает словарь ключей для подстановки
+func (sf *Packer) DictKeys() types.IDictKeys {
+	return sf.dictKeys
+}
+
+// Выполняет раунд сжатия
+func (sf *Packer) round() bool {
+	timeBeg := time.Now().Local().UnixMilli()
+	defer func() {
+		timeEnd := time.Now().Local().UnixMilli()
+		timeDelta := float32(timeEnd-timeBeg) / 1000
+		sf.timeTotal += timeDelta
+		ratio := (float32(len(sf.binData)) - float32(sf.originSize)) / float32(sf.originSize) * 100
+		log.Printf("Packer.Round(): timeTotal=%0.2fsec, timeRound=%0.2fsec, ration=%0.2f%%, totalChange=%v\n\n",
+			sf.timeTotal,
+			timeDelta,
+			ratio,
+			sf.totalChange)
+	}()
+	sf.dictUp()
+	if err := sf.frequencer.Run(); err != nil {
+		log.Printf("Packer.round(): in work IFrequencer, err=\n\t%v\n", err)
+		return false
+	}
+	sf.freqPair.SetKeys(sf.frequencer.Keys())
+	keyNext, err := sf.keyNext()
+	if err != nil {
+		log.Printf("Packer.round(): limit keys, err=\n\t%v\n", err)
+		return false
+	}
+	sf.changeKey(keyNext)
+	return true
+}
+
+// Заменяет частотную пару на указанный ключ
+func (sf *Packer) changeKey(key uint16) {
+	sf.binNew = sf.binNew[:0]
+	val0 := sf.binData[0]
+	ind := 1
+	val1 := sf.binData[ind]
+	fnNext := func() bool { // вычисляет сдвиг после замены
+		sf.binNew = append(sf.binNew, key)
+		if ind+1 >= len(sf.binData) { // Достигнут предел прохода
+			return false
+		}
+		ind++
+		val0 = sf.binData[ind]
+		if ind+1 >= len(sf.binData) { // Достигнут предел прохода
+			return false
+		}
+		ind++
+		val1 = sf.binData[ind]
+		return true
+	}
+	key0 := sf.freqPair.Key0_
+	key1 := sf.freqPair.Key1_
+	for ind < len(sf.binData) {
+		val1 = sf.binData[ind]
+		if val0 == key0 && val1 == key1 {
+			fnNext()
+			continue
+		}
+		sf.binNew = append(sf.binNew, val0)
+		val0 = val1
+		ind++
+	}
+	countChange := len(sf.binData) - len(sf.binNew)
+	sf.totalChange += countChange
+	log.Printf("Packer.changeKey(): len{old=%v, new=%v}, numChange=%v\n",
+		len(sf.binData),
+		len(sf.binNew),
+		countChange)
+	sf.binData = sf.binNew
+}
+
+// Ищёт следующий свободный ключ
+func (sf *Packer) keyNext() (uint16, error) {
+	key := uint16(0)
+	for {
+		if !sf.dictKeys.IsExists(key) {
+			return key, nil
+		}
+		key++
+		if key == math.MaxUint16 {
+			return 0, fmt.Errorf("Packer.keyNext(): key up to limit uint32")
+		}
+	}
+}
+
+// Собирает словарь ключей из текста
+func (sf *Packer) dictUp() {
+	// Собрать встречающиеся ключи
+	for _, val := range sf.binData {
+		if !sf.dictKeys.IsExists(val) {
+			sf.dictKeys.Add(val)
+			continue
+		}
+	}
+}

+ 24 - 2
internal/service/service.go

@@ -4,13 +4,15 @@ package service
 import (
 	"fmt"
 	"log"
+	"os"
 
 	"p78git.ddns.net/svi/packer/internal/packer"
+	"p78git.ddns.net/svi/packer/pkg/types"
 )
 
 // Service -- главный тип пакера
 type Service struct {
-	packer *packer.Packer
+	packer types.IPacker
 }
 
 // NewService -- возвращает новый сервис
@@ -22,10 +24,30 @@ func NewService() (*Service, error) {
 	if err != nil {
 		return nil, fmt.Errorf("NewService(): in create Packer, err=\n\t%w", err)
 	}
+	_ = types.IService(sf)
 	return sf, nil
 }
 
 // Run -- запускает сервис в работу
-func (sf *Service) Run() {
+func (sf *Service) Run() error {
 	log.Printf("Service.Run()\n")
+	cmd := os.Getenv("PACKER_CMD")
+	if cmd == "" {
+		return fmt.Errorf("Service.Run(): env PACKER_CMD not set")
+	}
+	switch cmd {
+	case "pack":
+		err := sf.packer.Run()
+		if err != nil {
+			return fmt.Errorf("Service.Run(): in run IPacker, err=\n\t%w", err)
+		}
+	default:
+		return fmt.Errorf("Service.Run(): unknown env PACKER_CMD(%q)", cmd)
+	}
+	return nil
+}
+
+// Packer -- возвращает упаковщик
+func (sf *Service) Packer() types.IPacker {
+	return sf.packer
 }

+ 15 - 0
pkg/types/idict_keys.go

@@ -0,0 +1,15 @@
+package types
+
+// IDictKeys -- словарь ключей
+type IDictKeys interface {
+	// Len -- возвращает число ключей в словаре
+	Len() int
+	// IsExists -- проверяет существует ли такой ключ
+	IsExists(uint16) bool
+	// Stat -- вываливает статистику
+	Stat() string
+	// Add -- добавляет ключ в словарь
+	Add(uint16)
+	// Keys -- возвращает словарь ключей
+	Keys() []uint16
+}

+ 9 - 0
pkg/types/ifrequencer.go

@@ -0,0 +1,9 @@
+package types
+
+// IFrequencer -- анализатор частот
+type IFrequencer interface {
+	// Run -- запускает частотник в работу
+	Run() error
+	// Keys -- возвращает подобранные ключи
+	Keys() (key0, key1 uint16)
+}

+ 13 - 0
pkg/types/ipacker.go

@@ -0,0 +1,13 @@
+package types
+
+// IPacker -- упаковщик
+type IPacker interface {
+	// DictKeys -- возвращает словарь ключей
+	DictKeys() IDictKeys
+	// BinData -- возвращает бинарные данные упаковки
+	BinData() []uint16
+	// Serv -- возвращает сервис
+	Serv() IService
+	// Run -- запуск пакера
+	Run() error
+}

+ 4 - 1
pkg/types/iservice.go

@@ -2,4 +2,7 @@
 package types
 
 // IService -- сервис
-type IService interface{}
+type IService interface {
+	// Packer - -возвращает упаковщик
+	Packer() IPacker
+}