mirror of
https://github.com/EchoVault/SugarDB.git
synced 2025-10-05 16:06:57 +08:00
Moved utils.go file to internals folder
This commit is contained in:
221
internal/utils.go
Normal file
221
internal/utils.go
Normal file
@@ -0,0 +1,221 @@
|
||||
// Copyright 2024 Kelvin Clement Mwinuka
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/echovault/echovault/pkg/utils"
|
||||
"io"
|
||||
"log"
|
||||
"math/big"
|
||||
"net"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sethvargo/go-retry"
|
||||
"github.com/tidwall/resp"
|
||||
)
|
||||
|
||||
func AdaptType(s string) interface{} {
|
||||
// Adapt the type of the parameter to string, float64 or int
|
||||
n, _, err := big.ParseFloat(s, 10, 256, big.RoundingMode(big.Exact))
|
||||
|
||||
if err != nil {
|
||||
return s
|
||||
}
|
||||
|
||||
if n.IsInt() {
|
||||
i, _ := n.Int64()
|
||||
return int(i)
|
||||
}
|
||||
|
||||
f, _ := n.Float64()
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func Decode(raw []byte) ([]string, error) {
|
||||
reader := resp.NewReader(bytes.NewReader(raw))
|
||||
|
||||
value, _, err := reader.ReadValue()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res []string
|
||||
for i := 0; i < len(value.Array()); i++ {
|
||||
res = append(res, value.Array()[i].String())
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func ReadMessage(r io.Reader) ([]byte, error) {
|
||||
reader := bufio.NewReader(r)
|
||||
|
||||
var res []byte
|
||||
|
||||
chunk := make([]byte, 8192)
|
||||
|
||||
for {
|
||||
n, err := reader.Read(chunk)
|
||||
if err != nil && errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = append(res, chunk...)
|
||||
if n < len(chunk) {
|
||||
break
|
||||
}
|
||||
clear(chunk)
|
||||
}
|
||||
|
||||
return bytes.Trim(res, "\x00"), nil
|
||||
}
|
||||
|
||||
func RetryBackoff(b retry.Backoff, maxRetries uint64, jitter, cappedDuration, maxDuration time.Duration) retry.Backoff {
|
||||
backoff := b
|
||||
if maxRetries > 0 {
|
||||
backoff = retry.WithMaxRetries(maxRetries, backoff)
|
||||
}
|
||||
if jitter > 0 {
|
||||
backoff = retry.WithJitter(jitter, backoff)
|
||||
}
|
||||
if cappedDuration > 0 {
|
||||
backoff = retry.WithCappedDuration(cappedDuration, backoff)
|
||||
}
|
||||
if maxDuration > 0 {
|
||||
backoff = retry.WithMaxDuration(maxDuration, backoff)
|
||||
}
|
||||
return backoff
|
||||
}
|
||||
|
||||
func GetIPAddress() (string, error) {
|
||||
conn, err := net.Dial("udp", "8.8.8.8:80")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer func() {
|
||||
if err = conn.Close(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
|
||||
localAddr := strings.Split(conn.LocalAddr().String(), ":")[0]
|
||||
|
||||
return localAddr, nil
|
||||
}
|
||||
|
||||
func GetSubCommand(command utils.Command, cmd []string) interface{} {
|
||||
if len(command.SubCommands) == 0 || len(cmd) < 2 {
|
||||
return nil
|
||||
}
|
||||
for _, subCommand := range command.SubCommands {
|
||||
if strings.EqualFold(subCommand.Command, cmd[1]) {
|
||||
return subCommand
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsWriteCommand(command utils.Command, subCommand utils.SubCommand) bool {
|
||||
return slices.Contains(append(command.Categories, subCommand.Categories...), utils.WriteCategory)
|
||||
}
|
||||
|
||||
func AbsInt(n int) int {
|
||||
if n < 0 {
|
||||
return -n
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// ParseMemory returns an integer representing the bytes in the memory string
|
||||
func ParseMemory(memory string) (uint64, error) {
|
||||
// Parse memory strings such as "100mb", "16gb"
|
||||
memString := memory[0 : len(memory)-2]
|
||||
bytesInt, err := strconv.ParseInt(memString, 10, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
memUnit := strings.ToLower(memory[len(memory)-2:])
|
||||
switch memUnit {
|
||||
case "kb":
|
||||
bytesInt *= 1024
|
||||
case "mb":
|
||||
bytesInt *= 1024 * 1024
|
||||
case "gb":
|
||||
bytesInt *= 1024 * 1024 * 1024
|
||||
case "tb":
|
||||
bytesInt *= 1024 * 1024 * 1024 * 1024
|
||||
case "pb":
|
||||
bytesInt *= 1024 * 1024 * 1024 * 1024 * 1024
|
||||
default:
|
||||
return 0, fmt.Errorf("memory unit %s not supported, use (kb, mb, gb, tb, pb) ", memUnit)
|
||||
}
|
||||
|
||||
return uint64(bytesInt), nil
|
||||
}
|
||||
|
||||
// IsMaxMemoryExceeded checks whether we have exceeded the current maximum memory limit.
|
||||
func IsMaxMemoryExceeded(maxMemory uint64) bool {
|
||||
if maxMemory == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
var memStats runtime.MemStats
|
||||
runtime.ReadMemStats(&memStats)
|
||||
|
||||
// If we're currently using less than the configured max memory, return false.
|
||||
if memStats.HeapInuse < maxMemory {
|
||||
return false
|
||||
}
|
||||
|
||||
// If we're currently using more than max memory, force a garbage collection before we start deleting keys.
|
||||
// This measure is to prevent deleting keys that may be important when some memory can be reclaimed
|
||||
// by just collecting garbage.
|
||||
runtime.GC()
|
||||
runtime.ReadMemStats(&memStats)
|
||||
|
||||
// Return true when whe are above or equal to max memory.
|
||||
return memStats.HeapInuse >= maxMemory
|
||||
}
|
||||
|
||||
// FilterExpiredKeys filters out keys that are already expired, so they are not persisted.
|
||||
func FilterExpiredKeys(state map[string]utils.KeyData) map[string]utils.KeyData {
|
||||
var keysToDelete []string
|
||||
for k, v := range state {
|
||||
// Skip keys with no expiry time.
|
||||
if v.ExpireAt == (time.Time{}) {
|
||||
continue
|
||||
}
|
||||
// If the key is already expired, mark it for deletion.
|
||||
if v.ExpireAt.Before(time.Now()) {
|
||||
keysToDelete = append(keysToDelete, k)
|
||||
}
|
||||
}
|
||||
for _, key := range keysToDelete {
|
||||
delete(state, key)
|
||||
}
|
||||
return state
|
||||
}
|
Reference in New Issue
Block a user