github.com/moreal/bencodex-go@v0.0.0-20231021172012-18277a477d15/internal/encoder/dictionary.go (about)

     1  package encoder
     2  
     3  import (
     4  	"sync"
     5  
     6  	"github.com/moreal/bencodex-go/internal"
     7  )
     8  
     9  const bytesLikeArrayLen = 20
    10  
    11  var stringsArrayPool = sync.Pool{
    12  	New: func() interface{} {
    13  		return &[bytesLikeArrayLen]internal.BencodexBytesLike{}
    14  	},
    15  }
    16  
    17  // -1 = left go first, 0 = equal, 1 = right go first
    18  func compareBytes(x, y []byte) int {
    19  	var minLen int
    20  	if len(x) > len(y) {
    21  		minLen = len(y)
    22  	} else {
    23  		minLen = len(x)
    24  	}
    25  
    26  	for i := 0; i < minLen; i++ {
    27  		if x[i] > y[i] {
    28  			return 1
    29  		} else if x[i] < y[i] {
    30  			return -1
    31  		}
    32  	}
    33  
    34  	if len(x) == len(y) {
    35  		return 0
    36  	}
    37  
    38  	if len(x) > len(y) {
    39  		return 1
    40  	}
    41  
    42  	return -1
    43  }
    44  
    45  func sortKeys(keys []internal.BencodexBytesLike) {
    46  	for i := 1; i < len(keys); i++ {
    47  		for j := i; j > 0; j-- {
    48  			if keys[j].IsString() && keys[j-1].IsBytes() {
    49  				break
    50  			} else if keys[j].IsBytes() && keys[j-1].IsString() {
    51  				keys[j], keys[j-1] = keys[j-1], keys[j]
    52  			} else if keys[j].IsBytes() && keys[j-1].IsBytes() {
    53  				res := compareBytes(keys[j-1].MustAsBytes(), keys[j].MustAsBytes())
    54  				if res >= 0 {
    55  					break
    56  				}
    57  			} else if keys[j].MustAsString() >= keys[j-1].MustAsString() {
    58  				break
    59  			}
    60  
    61  			keys[j], keys[j-1] = keys[j-1], keys[j]
    62  		}
    63  	}
    64  }
    65  
    66  func (e *Encoder) encodeDictionary(data map[internal.BencodexBytesLike]interface{}) error {
    67  	e.grow(1)
    68  	e.writeByte('d')
    69  	var keys []internal.BencodexBytesLike
    70  	if len(data) <= bytesLikeArrayLen {
    71  		stringsArray := stringsArrayPool.Get().(*[bytesLikeArrayLen]internal.BencodexBytesLike)
    72  		defer stringsArrayPool.Put(stringsArray)
    73  		keys = stringsArray[:0:len(data)]
    74  	} else {
    75  		keys = make([]internal.BencodexBytesLike, 0, len(data))
    76  	}
    77  	for key, _ := range data {
    78  		keys = append(keys, key)
    79  	}
    80  	sortKeys(keys)
    81  	for _, key := range keys {
    82  		e.encodeBytesLike(key)
    83  		err := e.encode(data[key])
    84  		if err != nil {
    85  			return err
    86  		}
    87  	}
    88  	e.grow(1)
    89  	e.writeByte('e')
    90  	return nil
    91  }