예제 #1
0
파일: strings.go 프로젝트: aegsea/rodis
func getbit(v resp.CommandArgs, ex *CommandExtras) error {
	ex.DB.RLock()
	defer ex.DB.RUnlock()

	exists, tipe, _ := ex.DB.Has(v[0])
	if !exists {
		return resp.ZeroInteger.WriteTo(ex.Buffer)
	}
	if tipe != storage.String {
		return resp.NewError(ErrWrongType).WriteTo(ex.Buffer)
	}

	val := ex.DB.GetString(v[0])

	offset, err := strconv.Atoi(string(v[1]))
	if err != nil {
		return resp.NewError(ErrNotValidInt).WriteTo(ex.Buffer)
	}

	if offset >= 8*len(val) {
		return resp.ZeroInteger.WriteTo(ex.Buffer)
	}

	byten := offset / 8
	pos := offset % 8

	k := val[byten] >> uint32(7-pos) & 0x01
	return resp.Integer(k).WriteTo(ex.Buffer)
}
예제 #2
0
파일: strings.go 프로젝트: aegsea/rodis
func setrange(v resp.CommandArgs, ex *CommandExtras) error {
	i64, err := strconv.ParseInt(string(v[1]), 10, 32)
	if err != nil {
		return resp.NewError(ErrNotValidInt).WriteTo(ex.Buffer)
	}
	offset := int(i64)
	if offset < 0 {
		return resp.NewError(ErrOffsetOutRange).WriteTo(ex.Buffer)
	}
	if offset+len(v[2]) > 536870912 { // 512M is the limit length
		return resp.NewError(ErrStringExccedLimit).WriteTo(ex.Buffer)
	}

	ex.DB.Lock()
	defer ex.DB.Unlock()

	exists, tipe, expireAt := ex.DB.Has(v[0])
	if exists && tipe != storage.String {
		return resp.NewError(ErrWrongType).WriteTo(ex.Buffer)
	}

	val := []byte("")
	if exists {
		val = ex.DB.GetString(v[0])
	}

	if len(val) < offset+len(v[2]) {
		val = append(val, make([]byte, len(v[2])+offset-len(val))...)
	}
	copy(val[offset:], v[2])

	ex.DB.PutString(v[0], val, expireAt)
	return resp.Integer(len(val)).WriteTo(ex.Buffer)
}
예제 #3
0
파일: hashes.go 프로젝트: aegsea/rodis
func hdel(v resp.CommandArgs, ex *CommandExtras) error {
	if len(v) < 2 {
		return resp.NewError(ErrFmtWrongNumberArgument, "hdel").WriteTo(ex.Buffer)
	}

	ex.DB.Lock()
	defer ex.DB.Unlock()

	keyExists, tipe, _ := ex.DB.Has(v[0])
	if !keyExists {
		return resp.ZeroInteger.WriteTo(ex.Buffer)
	}
	if keyExists && tipe != storage.Hash {
		return resp.NewError(ErrWrongType).WriteTo(ex.Buffer)
	}

	fields := [][]byte{}
	for _, field := range v[1:] {
		fields = append(fields, []byte(field))
	}
	hash := ex.DB.GetHashFields(v[0], fields)

	count := 0
	for _, value := range hash {
		if value != nil {
			count++
		}
	}
	ex.DB.DeleteHashFields(v[0], fields)
	return resp.Integer(count).WriteTo(ex.Buffer)
}
예제 #4
0
파일: hashes.go 프로젝트: aegsea/rodis
func hincrby(v resp.CommandArgs, ex *CommandExtras) error {
	by, err := strconv.ParseInt(v[2].String(), 10, 64)
	if err != nil {
		return resp.NewError(ErrNotValidInt).WriteTo(ex.Buffer)
	}

	ex.DB.Lock()
	defer ex.DB.Unlock()

	keyExists, tipe, expireAt := ex.DB.Has(v[0])
	if keyExists && tipe != storage.Hash {
		return resp.NewError(ErrWrongType).WriteTo(ex.Buffer)
	}

	hash := ex.DB.GetHashFields(v[0], [][]byte{v[1]})

	newVal := int64(0)
	if hash[string(v[1])] == nil {
		newVal += by
	} else {
		i, err := strconv.ParseInt(string(hash[string(v[1])]), 10, 64)
		if err != nil {
			return resp.NewError(ErrNotValidInt).WriteTo(ex.Buffer)
		}
		newVal = i + by
	}
	hash[string(v[1])] = []byte(strconv.FormatInt(newVal, 10))

	ex.DB.PutHash(v[0], hash, expireAt)
	return resp.Integer(newVal).WriteTo(ex.Buffer)
}
예제 #5
0
파일: keys.go 프로젝트: aegsea/rodis
func del(v resp.CommandArgs, ex *CommandExtras) error {
	if len(v) == 0 {
		return resp.NewError(ErrFmtWrongNumberArgument, "del").WriteTo(ex.Buffer)
	}

	ex.DB.Lock()
	defer ex.DB.Unlock()

	count := 0
	for _, key := range v {
		exists, tipe, _ := ex.DB.Has(key)
		if !exists {
			continue
		}
		switch tipe {
		case storage.String:
			ex.DB.DeleteString(key)
		case storage.Hash:
			ex.DB.DeleteHash(key)
		}

		count++
	}
	return resp.Integer(count).WriteTo(ex.Buffer)
}
예제 #6
0
파일: strings.go 프로젝트: aegsea/rodis
func bitcount(v resp.CommandArgs, ex *CommandExtras) error {
	if len(v) == 0 {
		return resp.NewError(ErrFmtWrongNumberArgument, "bitcount").WriteTo(ex.Buffer)
	}

	ex.DB.RLock()
	defer ex.DB.RUnlock()

	exists, tipe, _ := ex.DB.Has(v[0])
	if !exists {
		return resp.ZeroInteger.WriteTo(ex.Buffer)
	}
	if tipe != storage.String {
		return resp.NewError(ErrWrongType).WriteTo(ex.Buffer)
	}

	if len(v) != 1 && len(v) != 3 {
		return resp.NewError(ErrFmtSyntax).WriteTo(ex.Buffer)
	}

	val := ex.DB.GetString(v[0])

	start := 0
	end := len(val)
	var err error

	if len(v) == 3 {
		start, err = strconv.Atoi(string(v[1]))
		if err != nil {
			return resp.NewError(ErrNotValidInt).WriteTo(ex.Buffer)
		}

		end, err = strconv.Atoi(string(v[2]))
		if err != nil {
			return resp.NewError(ErrNotValidInt).WriteTo(ex.Buffer)
		}

		start, end = calcRange(start, end, len(val))
	}

	if end <= start {
		return resp.ZeroInteger.WriteTo(ex.Buffer)
	}

	sum := 0
	for _, b := range val[start:end] {
		sum += countSetBits[b]
	}
	return resp.Integer(sum).WriteTo(ex.Buffer)
}
예제 #7
0
파일: strings.go 프로젝트: aegsea/rodis
func setbit(v resp.CommandArgs, ex *CommandExtras) error {
	i64, err := strconv.ParseInt(string(v[1]), 10, 32)
	if err != nil {
		return resp.NewError(ErrNotValidInt).WriteTo(ex.Buffer)
	}
	offset := uint32(i64)
	pos := offset % 8
	byten := offset / 8

	if int(byten)+1 > STRLIMIT {
		return resp.NewError(ErrStringExccedLimit).WriteTo(ex.Buffer)
	}

	bit, err := strconv.Atoi(string(v[2]))
	if err != nil || bit != 0 && bit != 1 {
		return resp.NewError(ErrBitValueInvalid).WriteTo(ex.Buffer)
	}

	ex.DB.Lock()
	defer ex.DB.Unlock()

	exists, tipe, expireAt := ex.DB.Has(v[0])
	if exists && tipe != storage.String {
		return resp.NewError(ErrWrongType).WriteTo(ex.Buffer)
	}

	val := []byte("")
	if exists {
		val = ex.DB.GetString(v[0])
	}

	if uint32(len(val)) < byten+1 {
		val = append(val, make([]byte, int(byten)+1-len(val))...)
	}

	k := val[byten] >> uint32(7-pos) & 0x01

	switch bit {
	case 0:
		clear := byte(^(0x01 << (7 - pos)))
		val[byten] = val[byten] & clear
	case 1:
		set := byte(0x01 << (7 - pos))
		val[byten] = val[byten] | set
	}

	ex.DB.PutString(v[0], val, expireAt)
	return resp.Integer(k).WriteTo(ex.Buffer)
}
예제 #8
0
파일: strings.go 프로젝트: aegsea/rodis
func strlen(v resp.CommandArgs, ex *CommandExtras) error {
	ex.DB.RLock()
	defer ex.DB.RUnlock()

	exists, tipe, _ := ex.DB.Has(v[0])
	if !exists {
		return resp.ZeroInteger.WriteTo(ex.Buffer)
	}
	if tipe != storage.String {
		return resp.NewError(ErrWrongType).WriteTo(ex.Buffer)
	}

	val := ex.DB.GetString(v[0])
	return resp.Integer(len(val)).WriteTo(ex.Buffer)
}
예제 #9
0
파일: hashes.go 프로젝트: aegsea/rodis
func hstrlen(v resp.CommandArgs, ex *CommandExtras) error {
	ex.DB.RLock()
	defer ex.DB.RUnlock()

	keyExists, tipe, _ := ex.DB.Has(v[0])
	if !keyExists {
		return resp.ZeroInteger.WriteTo(ex.Buffer)
	}
	if keyExists && tipe != storage.Hash {
		return resp.NewError(ErrWrongType).WriteTo(ex.Buffer)
	}

	hash := ex.DB.GetHashFields(v[0], [][]byte{v[1]})
	return resp.Integer(len(hash[string(v[1])])).WriteTo(ex.Buffer)
}
예제 #10
0
파일: hashes.go 프로젝트: aegsea/rodis
func hlen(v resp.CommandArgs, ex *CommandExtras) error {
	ex.DB.RLock()
	defer ex.DB.RUnlock()

	keyExists, tipe, _ := ex.DB.Has(v[0])
	if !keyExists {
		return resp.ZeroInteger.WriteTo(ex.Buffer)
	}
	if keyExists && tipe != storage.Hash {
		return resp.NewError(ErrWrongType).WriteTo(ex.Buffer)
	}

	fields := ex.DB.GetHashFieldNames(v[0])
	return resp.Integer(len(fields)).WriteTo(ex.Buffer)
}
예제 #11
0
파일: keys.go 프로젝트: aegsea/rodis
func exists(v resp.CommandArgs, ex *CommandExtras) error {
	if len(v) == 0 {
		return resp.NewError(ErrFmtWrongNumberArgument, "exists").WriteTo(ex.Buffer)
	}

	ex.DB.RLock()
	defer ex.DB.RUnlock()

	count := 0
	for _, key := range v {
		exists, _, _ := ex.DB.Has(key)
		if !exists {
			continue
		}
		count++
	}
	return resp.Integer(count).WriteTo(ex.Buffer)
}
예제 #12
0
파일: strings.go 프로젝트: aegsea/rodis
// use appendx for append command, because append is a key word of golang
func appendx(v resp.CommandArgs, ex *CommandExtras) error {
	ex.DB.Lock()
	defer ex.DB.Unlock()

	exists, tipe, expireAt := ex.DB.Has(v[0])
	if exists && tipe != storage.String {
		return resp.NewError(ErrWrongType).WriteTo(ex.Buffer)
	}

	val := []byte("")
	if exists {
		val = ex.DB.GetString(v[0])
	}
	if len(val)+len(v[1]) > STRLIMIT {
		return resp.NewError(ErrStringExccedLimit).WriteTo(ex.Buffer)
	}

	val = append(val, v[1]...)
	ex.DB.PutString(v[0], val, expireAt)
	return resp.Integer(len(val)).WriteTo(ex.Buffer)
}
예제 #13
0
파일: strings.go 프로젝트: aegsea/rodis
func incrdecrHelper(v resp.CommandArgs, ex *CommandExtras, by int64) error {
	ex.DB.Lock()
	defer ex.DB.Unlock()

	exists, tipe, expireAt := ex.DB.Has(v[0])
	if exists && tipe != storage.String {
		return resp.NewError(ErrWrongType).WriteTo(ex.Buffer)
	}

	newVal := int64(0)
	if !exists {
		newVal += by
	} else {
		val := ex.DB.GetString(v[0])
		i, err := strconv.ParseInt(string(val), 10, 64)
		if err != nil {
			return resp.NewError(ErrNotValidInt).WriteTo(ex.Buffer)
		}
		newVal = i + by
	}

	ex.DB.PutString(v[0], []byte(strconv.FormatInt(newVal, 10)), expireAt)
	return resp.Integer(newVal).WriteTo(ex.Buffer)
}
예제 #14
0
파일: strings.go 프로젝트: aegsea/rodis
func bitpos(v resp.CommandArgs, ex *CommandExtras) error {
	if len(v) < 2 {
		return resp.NewError(ErrFmtWrongNumberArgument, "bitpos").WriteTo(ex.Buffer)
	}

	arg, err := strconv.Atoi(string(v[1]))
	if err != nil || arg != 0 && arg != 1 {
		return resp.NewError(ErrShouldBe0or1).WriteTo(ex.Buffer)
	}

	set := arg == 1   // set bit pos
	clear := arg == 0 // clear bit pos

	ex.DB.RLock()
	defer ex.DB.RUnlock()

	exists, tipe, _ := ex.DB.Has(v[0])
	if exists && tipe != storage.String {
		return resp.NewError(ErrWrongType).WriteTo(ex.Buffer)
	}

	// This is the same behavior as offical redis. Not sure why
	// not check the len(v) when key is missing
	if !exists && set {
		return resp.NegativeOneInteger.WriteTo(ex.Buffer)
	}
	if !exists && clear {
		return resp.ZeroInteger.WriteTo(ex.Buffer)
	}

	// Seam that: check the len(v) only when the key exists
	if len(v) > 4 {
		return resp.NewError(ErrFmtSyntax).WriteTo(ex.Buffer)
	}

	val := ex.DB.GetString(v[0])
	// Get the range.
	start := 0
	end := len(val)
	if len(v) >= 3 {
		start, err = strconv.Atoi(string(v[2]))
		if err != nil {
			return resp.NewError(ErrNotValidInt).WriteTo(ex.Buffer)
		}
	}
	if len(v) == 4 {
		end, err = strconv.Atoi(string(v[3]))
		if err != nil {
			return resp.NewError(ErrNotValidInt).WriteTo(ex.Buffer)
		}
	}
	start, end = calcRange(start, end, len(val))
	if end <= start {
		return resp.NegativeOneInteger.WriteTo(ex.Buffer)
	}

	// Get the postion in the range
	pos := 0
	found := false
	for _, b := range val[start:end] {
		if clear && posFirstClear[b] != 8 {
			found = true
			pos += posFirstClear[b]
			break
		}
		if set && posFirstSet[b] != -1 {
			found = true
			pos += posFirstSet[b]
			break
		}
		pos += 8 // not found, pos += 1*byte
	}

	if found {
		return resp.Integer(8*start + pos).WriteTo(ex.Buffer)
	}

	// From http://redis.io/commands/bitpos
	// If we look for set bits (the bit argument is 1) and the string is
	// empty or composed of just zero bytes, -1 is returned.
	if !found && set {
		return resp.NegativeOneInteger.WriteTo(ex.Buffer)
	}

	// If we look for clear bits (the bit argument is 0) and the string only
	// contains bit set to 1, the function returns the first bit not part of
	// the string on the right. So if the string is three bytes set to the
	// value 0xff the command BITPOS key 0 will return 24, since up to bit 23
	// all the bits are 1.
	// Basically, the function considers the right of the string as padded with
	// zeros if you look for clear bits and specify no range or the start argument
	// only.
	if !found && clear && len(v) < 4 { //len(v) < 4: no range 'end' specified
		return resp.Integer(8 * end).WriteTo(ex.Buffer)
	}
	// However, this behavior changes if you are looking for clear bits and
	// specify a range with both start and end. If no clear bit is found in
	// the specified range, the function returns -1 as the user specified a
	// clear range and there are no 0 bits in that range.
	if !found && clear && len(v) == 4 {
		return resp.NegativeOneInteger.WriteTo(ex.Buffer)
	}
	return resp.NegativeOneInteger.WriteTo(ex.Buffer) // Should NEVER called
}
예제 #15
0
파일: strings.go 프로젝트: aegsea/rodis
func bitop(v resp.CommandArgs, ex *CommandExtras) error {
	if len(v) < 3 {
		return resp.NewError(ErrFmtWrongNumberArgument, "bitop").WriteTo(ex.Buffer)
	}

	ex.DB.Lock()
	defer ex.DB.Unlock()

	op := strings.ToLower(string(v[0]))

	switch op {
	case "not":
		if len(v) > 3 {
			return resp.NewError(ErrBitOPNotError).WriteTo(ex.Buffer)
		}
		exists, tipe, _ := ex.DB.Has(v[2])
		if !exists {
			return resp.ZeroInteger.WriteTo(ex.Buffer)
		}
		if exists && tipe != storage.String {
			return resp.NewError(ErrWrongType).WriteTo(ex.Buffer)
		}

		val := ex.DB.GetString(v[2])
		destValue := make([]byte, len(val))
		for i, b := range val {
			destValue[i] = ^b
		}

		ex.DB.PutString(v[1], destValue, nil)
		return resp.Integer(len(destValue)).WriteTo(ex.Buffer)

	case "or", "and", "xor":
		var destValue []byte = nil
		for _, b := range v[2:] {
			exists, tipe, _ := ex.DB.Has(b)
			if exists && tipe != storage.String {
				return resp.NewError(ErrWrongType).WriteTo(ex.Buffer)
			}
			val := ex.DB.GetString(b)
			if exists && len(destValue) < len(val) {
				if len(destValue) == 0 { // loop first step
					destValue = append(destValue, val...)
					continue
				} else {
					destValue = append(destValue, make([]byte, len(val)-len(destValue))...)
				}
			}
			for i, _ := range destValue {
				s := byte(0)
				if exists && i < len(val) {
					s = val[i]
				}
				switch op {
				case "or":
					destValue[i] |= s
				case "and":
					destValue[i] &= s
				case "xor":
					destValue[i] ^= s
				}
			}
		}
		ex.DB.PutString(v[1], destValue, nil)
		return resp.Integer(len(destValue)).WriteTo(ex.Buffer)

	default:
		return resp.NewError(ErrSyntax).WriteTo(ex.Buffer)
	}
}