package main import ( "bufio" "fmt" "net" "os" "strings" "sync" "strconv" "io" ) type RedisDB struct { mu sync.Mutex data map[string]string } var db RedisDB func main() { db = RedisDB{data: make(map[string]string)} listener, err := net.Listen("tcp", ":6379") if err != nil { fmt.Println("error starting server: ", err) os.Exit(1) } defer listener.Close() fmt.Println("Redis clone listening on port 6379...") for { conn, err := listener.Accept() if err != nil { fmt.Println("Error accepting connection:", err) continue } go handleConnection(conn) } } func handleGet(conn net.Conn, key string) { db.mu.Lock() value, exists := db.data[key] db.mu.Unlock() if exists { conn.Write([]byte("+" + value + "\r\n")) } else { conn.Write([]byte("$-1\r\n")) } } func handleExists(conn net.Conn, key string){ db.mu.Lock() _, exists := db.data[key] db.mu.Unlock() if exists { conn.Write([]byte(":1\r\n")) } else { conn.Write([]byte(":0\r\n")) } } func handleSet(conn net.Conn, key, value string) { db.mu.Lock() db.data[key] = value db.mu.Unlock() conn.Write([]byte("+OK\r\n")) } func handleDel(conn net.Conn, key string){ db.mu.Lock() _, exists := db.data[key] if exists { delete(db.data, key) conn.Write([]byte(":1\r\n")) } else { conn.Write([]byte(":0\r\n")) } db.mu.Unlock() } func handleIncr(conn net.Conn, key string){ db.mu.Lock() value, exists := db.data[key] if exists { currentInt, err := strconv.Atoi(value) if err != nil { conn.Write([]byte("-ERR value is not an integer or out of range\r\n")) } newValueStr := strconv.Itoa(currentInt+1) db.data[key] = newValueStr conn.Write([]byte(":" + newValueStr + "\r\n")) }else{ db.data[key] = "1" conn.Write([]byte(":1\r\n")) } db.mu.Unlock() } func handleDecr(conn net.Conn, key string){ db.mu.Lock() value, exists := db.data[key] if exists { currentInt, err := strconv.Atoi(value) if err != nil { conn.Write([]byte("-ERR value is not an integer or out of range\r\n")) } newValueStr := strconv.Itoa(currentInt-1) db.data[key] = newValueStr conn.Write([]byte(":" + newValueStr + "\r\n")) }else{ db.data[key] = "-1" conn.Write([]byte(":-1\r\n")) } db.mu.Unlock() } func handlePing(conn net.Conn) { conn.Write([]byte("+PONG\r\n")) } func handleFlushall(conn net.Conn){ db.mu.Lock() db.data = make(map[string]string) conn.Write([]byte("+OK\r\n")) db.mu.Unlock() } func parseRESP(reader *bufio.Reader) ([]string, error) { line, err := reader.ReadString('\n') if err != nil { return nil, err } var args []string argC, _ := strconv.Atoi(strings.TrimSpace(line[1:])) for i := 0; i < argC; i++ { lengthLine, err := reader.ReadString('\n') if err != nil { return nil, err } strLen, _ := strconv.Atoi(strings.TrimSpace(lengthLine[1:])) buf := make([]byte, strLen) _, err = io.ReadFull(reader, buf) if err != nil { return nil, err } _, err = reader.Discard(2) if err != nil { return nil, err } args = append(args, string(buf)) } return args, nil } func handleConnection(conn net.Conn) { defer conn.Close() fmt.Println("Client connected:", conn.RemoteAddr()) reader := bufio.NewReader(conn) for { args, err := parseRESP(reader) if err != nil { fmt.Println("Client disconnected or error parsing:", conn.RemoteAddr(), err) return } if len(args) == 0 { continue } fmt.Printf("Parsed RESP Arguments: %v\n", args) command := strings.ToUpper(args[0]) switch { case command == "GET" && len(args) == 2: handleGet(conn, args[1]) case command == "SET" && len(args) == 3: handleSet(conn, args[1], args[2]) case command == "EXISTS" && len(args) == 2: handleExists(conn, args[1]) case command == "DEL" && len(args) == 2: handleDel(conn, args[1]) case command == "INCR" && len(args) == 2: handleIncr(conn, args[1]) case command == "DECR" && len(args) == 2: handleDecr(conn, args[1]) case command == "PING" && len(args) == 1: handlePing(conn) case command == "FLUSHALL" && len(args) == 1: handleFlushall(conn) default: conn.Write([]byte("-ERR unknown command or wrong arguments\r\n")) } } }