func (resp *Response) Read(b *bufio.Reader) error { resp.items = make(map[string]*Item, 1) for { s, e := b.ReadString('\n') if e != nil { log.Print("read response line failed", e) return e } parts := strings.Fields(s) if len(parts) < 1 { return errors.New("invalid response") } resp.status = parts[0] switch resp.status { case "VALUE": if len(parts) < 3 { return errors.New("invalid response") } key := parts[1] // check key length length, e2 := strconv.Atoi(parts[2]) if e2 != nil { return errors.New("invalid response") } if length > MaxBodyLength { return errors.New("body too large") } item := &Item{} // FIXME if length > AllocLimit { item.alloc = cmem.Alloc(uintptr(length)) item.Body = (*[1 << 30]byte)(unsafe.Pointer(item.alloc))[:length] (*reflect.SliceHeader)(unsafe.Pointer(&item.Body)).Cap = length runtime.SetFinalizer(item, func(item *Item) { if item.alloc != nil { //log.Print("free by finalizer: ", cap(item.Body)) cmem.Free(item.alloc, uintptr(cap(item.Body))) item.Body = nil item.alloc = nil } }) } else { item.Body = make([]byte, length) } if _, e = io.ReadFull(b, item.Body); e != nil { return e } b.ReadByte() // \r b.ReadByte() // \n resp.items[key] = item continue case "STAT": if len(parts) != 3 { return errors.New("invalid response") } var item Item item.Body = []byte(parts[2]) resp.items[parts[1]] = &item continue case "END": case "STORED", "NOT_STORED", "DELETED", "NOT_FOUND": case "OK": case "ERROR", "SERVER_ERROR", "CLIENT_ERROR": if len(parts) > 1 { resp.msg = parts[1] } log.Print("error:", resp) default: // try to convert to int _, err := strconv.Atoi(resp.status) if err != nil { log.Print("unknown status:", s, resp.status) return errors.New("unknown response:" + resp.status) } } break } return nil }
func (req *Request) Read(b *bufio.Reader) (e error) { var s string if s, e = b.ReadString('\n'); e != nil { return e } if !strings.HasSuffix(s, "\r\n") { return errors.New("not completed command") } parts := strings.Fields(s) if len(parts) < 1 { return errors.New("invalid cmd") } req.Cmd = parts[0] switch req.Cmd { case "get": if len(parts) != 2 { return errors.New("invalid cmd") } req.Key = parts[1] case "set": if len(parts) != 3 { return errors.New("invalid cmd") } req.Key = parts[1] req.Item = &Item{} item := req.Item length, e := strconv.Atoi(parts[2]) if e != nil { return e } if length > MaxBodyLength { return errors.New("body too large") } // FIXME if length > AllocLimit { item.alloc = cmem.Alloc(uintptr(length)) item.Body = (*[1 << 30]byte)(unsafe.Pointer(item.alloc))[:length] (*reflect.SliceHeader)(unsafe.Pointer(&item.Body)).Cap = length runtime.SetFinalizer(item, func(item *Item) { if item.alloc != nil { //log.Print("free by finalizer: ", cap(item.Body)) cmem.Free(item.alloc, uintptr(cap(item.Body))) item.Body = nil item.alloc = nil } }) } else { item.Body = make([]byte, length) } if _, e = io.ReadFull(b, item.Body); e != nil { return e } b.ReadByte() // \r b.ReadByte() // \n case "delete": if len(parts) != 2 { return errors.New("invalid cmd") } req.Key = parts[1] case "stats": case "quit", "version", "flush_all": default: log.Print("unknown command", req.Cmd) return errors.New("unknown command: " + req.Cmd) } return }