How Redis SETBIT Changes a Single Bit Inside a String

Published:

The SETBIT command updates a bit at a specified offset within the string value stored at a key.

Syntax:

setbit key offset value

It returns the bit that was previously stored at that offset.

Bit layout example

Take the binary representation of "abc" as an example: in memory it appears as 011000010110001001100011. In that sequence, the 9th bit is 1. If the goal is to change it to 0, Redis handles the operation in several steps.

First, it checks whether the offset is valid. Since one byte contains 8 bits and a Redis string can be at most 512 MB, an offset is invalid when offset / 8 exceeds 512 MB.

Next, Redis verifies the bit value being written. A bit can only be 0 or 1; any other input is invalid. In the source, on represents the input value:

if (on & ~1) {
        addReplyError(c,err);
        return;
    }

Because each byte holds 8 bits, modifying the bit at position offset begins by locating the corresponding byte. Redis computes offset / 8 to find the byte index, stores that index in byte, and reads the byte into byteval. For example, to modify the 9th bit, it must first fetch the second byte.

byte = bitoffset >> 3;//一个字节是8位,现在需要除以8,以定位到第byte个字节上
byteval = ((uint8_t*)o->ptr)[byte];//取出第byte个字节

After byteval is retrieved, Redis determines the original value of the target bit so it can return that value as the command result. It takes offset % 8 and stores it in bit, where bit refers to the position counted from the low end of byteval. If the result of byteval AND the bit mask is greater than 0, the original bit was 1; otherwise it was 0.

bit = 7 - (bitoffset & 0x7); //offset对8取模
    bitval = byteval & (1 << bit); //1<<bit位表示将1从低位向左移bit位,获取到第bit位

The final step is to write the new bit value. Redis first clears the target bit in byteval, then sets it according to the new value. Since on & 0x1 can only produce 0x1 or 0x0, shifting it left by bit positions and OR-ing it with byteval produces the updated byte. Writing that byte back to position byte completes the SETBIT operation.

byteval &= ~(1 << bit); //1左移bit位,取反与原值相与,即将原值的低bit位赋值为0
    byteval |= ((on & 0x1) << bit); //on&0x1的值为要修改后的值,左移bit位,与原值相或
    ((uint8_t*)o->ptr)[byte] = byteval;

In short, SETBIT does not manipulate the whole string directly. It finds the target byte, reads the current bit, returns the previous value, and then updates only that specific bit in place.