Skip to main content
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
rdr
io.Reader
required
The underlying reader (typically a network connection)
return
*Parser
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)
return
[]string
Array of parsed strings (command and arguments)
error
error
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)
return
[]string
Array elements as strings
error
error
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)
return
[]byte
The parsed bulk string as bytes
error
error
Error if the bulk string format is invalid
RESP Format:
$6\r\nfoobar\r\n
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)
return
int
The parsed length value
error
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
ele
string
required
The string to encode
return
string
RESP-encoded bulk 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
arr
[]string
required
The string slice to encode
return
[]byte
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
arr
[]string
required
Array of pre-encoded RESP strings
return
[]byte
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
num
int
required
The integer to encode
return
[]byte
RESP-encoded integer
Example:
encoded := resp.ToInt(42)
// Returns: ":42\r\n"

Config

Configuration file parser (separate from RESP protocol).
File
string
Path to the configuration file
Pair
map[string]string
Parsed configuration key-value pairs

NewConfig

Creates a new configuration parser.
func NewConfig(file string) *Config
file
string
required
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:
TypeFirst ByteExample
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

Build docs developers (and LLMs) love