The resp package implements parsing and encoding for the Redis Serialization Protocol (RESP), enabling communication between clients and the ValKeyper server.
Parser
The RESP protocol parser built on top of bufio.Reader.
type Parser struct {
*bufio.Reader
}
NewParser
Creates a new RESP parser from an io.Reader.
func NewParser(rdr io.Reader) *Parser
The underlying reader (typically a network connection)
A new Parser instance wrapping the reader
Example:
conn, _ := net.Dial("tcp", "localhost:6379")
parser := resp.NewParser(conn)
Parse
Parses the next RESP-encoded command or data from the stream.
func (p *Parser) Parse() ([]string, error)
Array of parsed strings (command and arguments)
Error if parsing fails or EOF is reached
Supported RESP Types:
- Arrays (
*) - Parsed into string slices
- Bulk Strings (
$) - Parsed into string elements
Example:
parser := resp.NewParser(conn)
command, err := parser.Parse()
if err != nil {
log.Fatal(err)
}
// command might be: ["SET", "key", "value"]
ParseArray
Parses a RESP array into a slice of strings.
func (p *Parser) ParseArray() ([]string, error)
Array elements as strings
Error if the array format is invalid
RESP Format:
*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n
Example:
// Assuming parser is positioned after reading '*'
array, err := parser.ParseArray()
// Returns: ["SET", "key", "value"]
ParseBulkString
Parses a RESP bulk string into a byte slice.
func (p *Parser) ParseBulkString() ([]byte, error)
The parsed bulk string as bytes
Error if the bulk string format is invalid
RESP Format:
Example:
// Assuming parser is positioned after reading '$'
data, err := parser.ParseBulkString()
// Returns: []byte("foobar")
GetLength
Reads a length-encoded integer from the stream.
func (p *Parser) GetLength() (int, error)
Error if the length cannot be parsed
Example:
// Reading "$5\r\nhello\r\n"
length, _ := parser.GetLength() // Returns: 5
Encoding Functions
Functions to encode Go data structures into RESP format.
ToBulkString
Encodes a string as a RESP bulk string.
func ToBulkString(ele string) string
Example:
encoded := resp.ToBulkString("hello")
// Returns: "$5\r\nhello\r\n"
ToArray
Encodes a string slice as a RESP array of bulk strings.
func ToArray(arr []string) []byte
The string slice to encode
RESP-encoded array as bytes
Example:
command := []string{"SET", "key", "value"}
encoded := resp.ToArray(command)
// Returns: "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n"
ToArrayAnyType
Encodes an array where elements are already RESP-encoded.
func ToArrayAnyType(arr []string) []byte
Array of pre-encoded RESP strings
RESP-encoded array as bytes
Example:
elements := []string{
resp.ToBulkString("key1"),
resp.ToBulkString("value1"),
}
encoded := resp.ToArrayAnyType(elements)
ToInt
Encodes an integer as a RESP integer.
func ToInt(num int) []byte
Example:
encoded := resp.ToInt(42)
// Returns: ":42\r\n"
Config
Configuration file parser (separate from RESP protocol).
Path to the configuration file
Parsed configuration key-value pairs
NewConfig
Creates a new configuration parser.
func NewConfig(file string) *Config
Path to the configuration file
Example:
config := resp.NewConfig("/etc/redis/redis.conf")
config.Marshal()
port := config.Pair["port"]
Complete Example
Here’s a complete example showing parsing and encoding:
package main
import (
"fmt"
"net"
"github.com/codecrafters-io/redis-starter-go/app/resp"
)
func handleClient(conn net.Conn) {
defer conn.Close()
parser := resp.NewParser(conn)
// Parse incoming command
command, err := parser.Parse()
if err != nil {
return
}
// Process command
switch command[0] {
case "PING":
// Send simple string response
conn.Write([]byte("+PONG\r\n"))
case "ECHO":
// Echo back the message
response := resp.ToBulkString(command[1])
conn.Write([]byte(response))
case "GET":
// Return a value
value := "example-value"
response := resp.ToBulkString(value)
conn.Write([]byte(response))
case "SET":
// Acknowledge the set
conn.Write([]byte("+OK\r\n"))
case "KEYS":
// Return array of keys
keys := []string{"key1", "key2", "key3"}
response := resp.ToArray(keys)
conn.Write(response)
}
}
func main() {
listener, _ := net.Listen("tcp", ":6379")
defer listener.Close()
for {
conn, _ := listener.Accept()
go handleClient(conn)
}
}
RESP Protocol Reference
The parser handles these RESP data types:
| Type | First Byte | Example |
|---|
| Simple String | + | +OK\r\n |
| Error | - | -ERR unknown command\r\n |
| Integer | : | :1000\r\n |
| Bulk String | $ | $5\r\nhello\r\n |
| Array | * | *2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n |
| Null | $ | $-1\r\n |