1package miniredis 2 3import ( 4 "sort" 5 "strconv" 6 "time" 7) 8 9func (db *RedisDB) exists(k string) bool { 10 _, ok := db.keys[k] 11 return ok 12} 13 14// t gives the type of a key, or "" 15func (db *RedisDB) t(k string) string { 16 return db.keys[k] 17} 18 19// allKeys returns all keys. Sorted. 20func (db *RedisDB) allKeys() []string { 21 res := make([]string, 0, len(db.keys)) 22 for k := range db.keys { 23 res = append(res, k) 24 } 25 sort.Strings(res) // To make things deterministic. 26 return res 27} 28 29// flush removes all keys and values. 30func (db *RedisDB) flush() { 31 db.keys = map[string]string{} 32 db.stringKeys = map[string]string{} 33 db.hashKeys = map[string]hashKey{} 34 db.listKeys = map[string]listKey{} 35 db.setKeys = map[string]setKey{} 36 db.sortedsetKeys = map[string]sortedSet{} 37 db.ttl = map[string]time.Duration{} 38} 39 40// move something to another db. Will return ok. Or not. 41func (db *RedisDB) move(key string, to *RedisDB) bool { 42 if _, ok := to.keys[key]; ok { 43 return false 44 } 45 46 t, ok := db.keys[key] 47 if !ok { 48 return false 49 } 50 to.keys[key] = db.keys[key] 51 switch t { 52 case "string": 53 to.stringKeys[key] = db.stringKeys[key] 54 case "hash": 55 to.hashKeys[key] = db.hashKeys[key] 56 case "list": 57 to.listKeys[key] = db.listKeys[key] 58 case "set": 59 to.setKeys[key] = db.setKeys[key] 60 case "zset": 61 to.sortedsetKeys[key] = db.sortedsetKeys[key] 62 default: 63 panic("unhandled key type") 64 } 65 to.keyVersion[key]++ 66 if v, ok := db.ttl[key]; ok { 67 to.ttl[key] = v 68 } 69 db.del(key, true) 70 return true 71} 72 73func (db *RedisDB) rename(from, to string) { 74 db.del(to, true) 75 switch db.t(from) { 76 case "string": 77 db.stringKeys[to] = db.stringKeys[from] 78 case "hash": 79 db.hashKeys[to] = db.hashKeys[from] 80 case "list": 81 db.listKeys[to] = db.listKeys[from] 82 case "set": 83 db.setKeys[to] = db.setKeys[from] 84 case "zset": 85 db.sortedsetKeys[to] = db.sortedsetKeys[from] 86 default: 87 panic("missing case") 88 } 89 db.keys[to] = db.keys[from] 90 db.keyVersion[to]++ 91 db.ttl[to] = db.ttl[from] 92 93 db.del(from, true) 94} 95 96func (db *RedisDB) del(k string, delTTL bool) { 97 if !db.exists(k) { 98 return 99 } 100 t := db.t(k) 101 delete(db.keys, k) 102 db.keyVersion[k]++ 103 if delTTL { 104 delete(db.ttl, k) 105 } 106 switch t { 107 case "string": 108 delete(db.stringKeys, k) 109 case "hash": 110 delete(db.hashKeys, k) 111 case "list": 112 delete(db.listKeys, k) 113 case "set": 114 delete(db.setKeys, k) 115 case "zset": 116 delete(db.sortedsetKeys, k) 117 default: 118 panic("Unknown key type: " + t) 119 } 120} 121 122// stringGet returns the string key or "" on error/nonexists. 123func (db *RedisDB) stringGet(k string) string { 124 if t, ok := db.keys[k]; !ok || t != "string" { 125 return "" 126 } 127 return db.stringKeys[k] 128} 129 130// stringSet force set()s a key. Does not touch expire. 131func (db *RedisDB) stringSet(k, v string) { 132 db.del(k, false) 133 db.keys[k] = "string" 134 db.stringKeys[k] = v 135 db.keyVersion[k]++ 136} 137 138// change int key value 139func (db *RedisDB) stringIncr(k string, delta int) (int, error) { 140 v := 0 141 if sv, ok := db.stringKeys[k]; ok { 142 var err error 143 v, err = strconv.Atoi(sv) 144 if err != nil { 145 return 0, ErrIntValueError 146 } 147 } 148 v += delta 149 db.stringSet(k, strconv.Itoa(v)) 150 return v, nil 151} 152 153// change float key value 154func (db *RedisDB) stringIncrfloat(k string, delta float64) (float64, error) { 155 v := 0.0 156 if sv, ok := db.stringKeys[k]; ok { 157 var err error 158 v, err = strconv.ParseFloat(sv, 64) 159 if err != nil { 160 return 0, ErrFloatValueError 161 } 162 } 163 v += delta 164 db.stringSet(k, formatFloat(v)) 165 return v, nil 166} 167 168// listLpush is 'left push', aka unshift. Returns the new length. 169func (db *RedisDB) listLpush(k, v string) int { 170 l, ok := db.listKeys[k] 171 if !ok { 172 db.keys[k] = "list" 173 } 174 l = append([]string{v}, l...) 175 db.listKeys[k] = l 176 db.keyVersion[k]++ 177 return len(l) 178} 179 180// 'left pop', aka shift. 181func (db *RedisDB) listLpop(k string) string { 182 l := db.listKeys[k] 183 el := l[0] 184 l = l[1:] 185 if len(l) == 0 { 186 db.del(k, true) 187 } else { 188 db.listKeys[k] = l 189 } 190 db.keyVersion[k]++ 191 return el 192} 193 194func (db *RedisDB) listPush(k string, v ...string) int { 195 l, ok := db.listKeys[k] 196 if !ok { 197 db.keys[k] = "list" 198 } 199 l = append(l, v...) 200 db.listKeys[k] = l 201 db.keyVersion[k]++ 202 return len(l) 203} 204 205func (db *RedisDB) listPop(k string) string { 206 l := db.listKeys[k] 207 el := l[len(l)-1] 208 l = l[:len(l)-1] 209 if len(l) == 0 { 210 db.del(k, true) 211 } else { 212 db.listKeys[k] = l 213 db.keyVersion[k]++ 214 } 215 return el 216} 217 218// setset replaces a whole set. 219func (db *RedisDB) setSet(k string, set setKey) { 220 db.keys[k] = "set" 221 db.setKeys[k] = set 222 db.keyVersion[k]++ 223} 224 225// setadd adds members to a set. Returns nr of new keys. 226func (db *RedisDB) setAdd(k string, elems ...string) int { 227 s, ok := db.setKeys[k] 228 if !ok { 229 s = setKey{} 230 db.keys[k] = "set" 231 } 232 added := 0 233 for _, e := range elems { 234 if _, ok := s[e]; !ok { 235 added++ 236 } 237 s[e] = struct{}{} 238 } 239 db.setKeys[k] = s 240 db.keyVersion[k]++ 241 return added 242} 243 244// setrem removes members from a set. Returns nr of deleted keys. 245func (db *RedisDB) setRem(k string, fields ...string) int { 246 s, ok := db.setKeys[k] 247 if !ok { 248 return 0 249 } 250 removed := 0 251 for _, f := range fields { 252 if _, ok := s[f]; ok { 253 removed++ 254 delete(s, f) 255 } 256 } 257 if len(s) == 0 { 258 db.del(k, true) 259 } else { 260 db.setKeys[k] = s 261 } 262 db.keyVersion[k]++ 263 return removed 264} 265 266// All members of a set. 267func (db *RedisDB) setMembers(k string) []string { 268 set := db.setKeys[k] 269 members := make([]string, 0, len(set)) 270 for k := range set { 271 members = append(members, k) 272 } 273 sort.Strings(members) 274 return members 275} 276 277// Is a SET value present? 278func (db *RedisDB) setIsMember(k, v string) bool { 279 set, ok := db.setKeys[k] 280 if !ok { 281 return false 282 } 283 _, ok = set[v] 284 return ok 285} 286 287// hashFields returns all (sorted) keys ('fields') for a hash key. 288func (db *RedisDB) hashFields(k string) []string { 289 v := db.hashKeys[k] 290 r := make([]string, 0, len(v)) 291 for k := range v { 292 r = append(r, k) 293 } 294 sort.Strings(r) 295 return r 296} 297 298// hashGet a value 299func (db *RedisDB) hashGet(key, field string) string { 300 return db.hashKeys[key][field] 301} 302 303// hashSet returns whether the key already existed 304func (db *RedisDB) hashSet(k, f, v string) bool { 305 if t, ok := db.keys[k]; ok && t != "hash" { 306 db.del(k, true) 307 } 308 db.keys[k] = "hash" 309 if _, ok := db.hashKeys[k]; !ok { 310 db.hashKeys[k] = map[string]string{} 311 } 312 _, ok := db.hashKeys[k][f] 313 db.hashKeys[k][f] = v 314 db.keyVersion[k]++ 315 return ok 316} 317 318// hashIncr changes int key value 319func (db *RedisDB) hashIncr(key, field string, delta int) (int, error) { 320 v := 0 321 if h, ok := db.hashKeys[key]; ok { 322 if f, ok := h[field]; ok { 323 var err error 324 v, err = strconv.Atoi(f) 325 if err != nil { 326 return 0, ErrIntValueError 327 } 328 } 329 } 330 v += delta 331 db.hashSet(key, field, strconv.Itoa(v)) 332 return v, nil 333} 334 335// hashIncrfloat changes float key value 336func (db *RedisDB) hashIncrfloat(key, field string, delta float64) (float64, error) { 337 v := 0.0 338 if h, ok := db.hashKeys[key]; ok { 339 if f, ok := h[field]; ok { 340 var err error 341 v, err = strconv.ParseFloat(f, 64) 342 if err != nil { 343 return 0, ErrFloatValueError 344 } 345 } 346 } 347 v += delta 348 db.hashSet(key, field, formatFloat(v)) 349 return v, nil 350} 351 352// sortedSet set returns a sortedSet as map 353func (db *RedisDB) sortedSet(key string) map[string]float64 { 354 ss := db.sortedsetKeys[key] 355 return map[string]float64(ss) 356} 357 358// ssetSet sets a complete sorted set. 359func (db *RedisDB) ssetSet(key string, sset sortedSet) { 360 db.keys[key] = "zset" 361 db.keyVersion[key]++ 362 db.sortedsetKeys[key] = sset 363} 364 365// ssetAdd adds member to a sorted set. Returns whether this was a new member. 366func (db *RedisDB) ssetAdd(key string, score float64, member string) bool { 367 ss, ok := db.sortedsetKeys[key] 368 if !ok { 369 ss = newSortedSet() 370 db.keys[key] = "zset" 371 } 372 _, ok = ss[member] 373 ss[member] = score 374 db.sortedsetKeys[key] = ss 375 db.keyVersion[key]++ 376 return !ok 377} 378 379// All members from a sorted set, ordered by score. 380func (db *RedisDB) ssetMembers(key string) []string { 381 ss, ok := db.sortedsetKeys[key] 382 if !ok { 383 return nil 384 } 385 elems := ss.byScore(asc) 386 members := make([]string, 0, len(elems)) 387 for _, e := range elems { 388 members = append(members, e.member) 389 } 390 return members 391} 392 393// All members+scores from a sorted set, ordered by score. 394func (db *RedisDB) ssetElements(key string) ssElems { 395 ss, ok := db.sortedsetKeys[key] 396 if !ok { 397 return nil 398 } 399 return ss.byScore(asc) 400} 401 402// ssetCard is the sorted set cardinality. 403func (db *RedisDB) ssetCard(key string) int { 404 ss := db.sortedsetKeys[key] 405 return ss.card() 406} 407 408// ssetRank is the sorted set rank. 409func (db *RedisDB) ssetRank(key, member string, d direction) (int, bool) { 410 ss := db.sortedsetKeys[key] 411 return ss.rankByScore(member, d) 412} 413 414// ssetScore is sorted set score. 415func (db *RedisDB) ssetScore(key, member string) float64 { 416 ss := db.sortedsetKeys[key] 417 return ss[member] 418} 419 420// ssetRem is sorted set key delete. 421func (db *RedisDB) ssetRem(key, member string) bool { 422 ss := db.sortedsetKeys[key] 423 _, ok := ss[member] 424 delete(ss, member) 425 if len(ss) == 0 { 426 // Delete key on removal of last member 427 db.del(key, true) 428 } 429 return ok 430} 431 432// ssetExists tells if a member exists in a sorted set. 433func (db *RedisDB) ssetExists(key, member string) bool { 434 ss := db.sortedsetKeys[key] 435 _, ok := ss[member] 436 return ok 437} 438 439// ssetIncrby changes float sorted set score. 440func (db *RedisDB) ssetIncrby(k, m string, delta float64) float64 { 441 ss, ok := db.sortedsetKeys[k] 442 if !ok { 443 ss = newSortedSet() 444 db.keys[k] = "zset" 445 db.sortedsetKeys[k] = ss 446 } 447 448 v, _ := ss.get(m) 449 v += delta 450 ss.set(v, m) 451 db.keyVersion[k]++ 452 return v 453} 454 455// setDiff implements the logic behind SDIFF* 456func (db *RedisDB) setDiff(keys []string) (setKey, error) { 457 key := keys[0] 458 keys = keys[1:] 459 if db.exists(key) && db.t(key) != "set" { 460 return nil, ErrWrongType 461 } 462 s := setKey{} 463 for k := range db.setKeys[key] { 464 s[k] = struct{}{} 465 } 466 for _, sk := range keys { 467 if !db.exists(sk) { 468 continue 469 } 470 if db.t(sk) != "set" { 471 return nil, ErrWrongType 472 } 473 for e := range db.setKeys[sk] { 474 delete(s, e) 475 } 476 } 477 return s, nil 478} 479 480// setInter implements the logic behind SINTER* 481func (db *RedisDB) setInter(keys []string) (setKey, error) { 482 key := keys[0] 483 keys = keys[1:] 484 if !db.exists(key) { 485 return setKey{}, nil 486 } 487 if db.t(key) != "set" { 488 return nil, ErrWrongType 489 } 490 s := setKey{} 491 for k := range db.setKeys[key] { 492 s[k] = struct{}{} 493 } 494 for _, sk := range keys { 495 if !db.exists(sk) { 496 return setKey{}, nil 497 } 498 if db.t(sk) != "set" { 499 return nil, ErrWrongType 500 } 501 other := db.setKeys[sk] 502 for e := range s { 503 if _, ok := other[e]; ok { 504 continue 505 } 506 delete(s, e) 507 } 508 } 509 return s, nil 510} 511 512// setUnion implements the logic behind SUNION* 513func (db *RedisDB) setUnion(keys []string) (setKey, error) { 514 key := keys[0] 515 keys = keys[1:] 516 if db.exists(key) && db.t(key) != "set" { 517 return nil, ErrWrongType 518 } 519 s := setKey{} 520 for k := range db.setKeys[key] { 521 s[k] = struct{}{} 522 } 523 for _, sk := range keys { 524 if !db.exists(sk) { 525 continue 526 } 527 if db.t(sk) != "set" { 528 return nil, ErrWrongType 529 } 530 for e := range db.setKeys[sk] { 531 s[e] = struct{}{} 532 } 533 } 534 return s, nil 535} 536 537// fastForward proceeds the current timestamp with duration, works as a time machine 538func (db *RedisDB) fastForward(duration time.Duration) { 539 for _, key := range db.allKeys() { 540 if value, ok := db.ttl[key]; ok { 541 db.ttl[key] = value - duration 542 db.checkTTL(key) 543 } 544 } 545} 546 547func (db *RedisDB) checkTTL(key string) { 548 if v, ok := db.ttl[key]; ok && v <= 0 { 549 db.del(key, true) 550 } 551} 552