Log in to vote

Select range of bits from string?

Asked by
eLunate 5136 Trusted Moderation Voter Community Moderator
5 years ago

Okay, so you know how strings are made up of characters, and all those characters are made up of a single byte each. Assuming I'm using a string to represent a ton of bits, how can I get the values for a range of the bits? So say, I wanted the bits from 2 to 13 for some obscure reason, how can I take my string and extract all of the bits in that range?

As an example, "FF" is 0000111100001111 in binary. If I wanted to select bits 2 -> 13 in that, I'd get 000111100001. And then somewhere along the line somebody decides whether to pad the 0s to the left or to the right when converting it back to the string but that's not a problem.

Extra useful if an explanation includes how to turn the range into a boolean array (I have a solution for this bit but it's not particularly efficient).

If you're needing a use case, I'm planning on creating my own replication because Roblox networking sucks.

Once you get the binary value, you could use the :sub. For example, if it was in a string value: print(game.ServerStorage.IntValue.Value:sub(2,13)) Ethan_Waike 156 — 5y
Hm, I could convert it to binary, but that would require evaluating the values of the entire string and I'm hoping not to do that. eLunate 5136 — 5y
I'm not sure how you would do this without first converting it to a binary format. I don't know of any built-in method that will extract such data from a variable/literal. BlackJPI 2653 — 5y
I can turn it into an array of bytes with {string.byte()} but that only gets me so far. eLunate 5136 — 5y

1 answer

Log in to vote
Answered by 5 years ago

Right, seems like I have came up with some seemingly efficient solution which returns the bits as table of booleans.

First thing you should do, is find the bytes that fall within the begin and end bit range, so no processing power is wasted converting unnecessary bytes to bits. After that is done, you can start converting the bytes into bits.

function getBits( str, beginBit, endBit )
    -- A reference table for bit values(reading from table is significantly faster than using power operator)
    local byte_t = { 1, 2, 4, 8, 16, 32, 64, 128 }

    -- Finding out actual bytes that we are interested in
    local beginChar = math.ceil( beginBit/8 )
    local endChar = math.ceil( endBit/8 )
    local range = str:sub( beginChar, endChar )

    -- I will convert and directly set the captured bits, so there is no reason to loop several times and what not
    local bits = {}
    -- Since I will directly store the result, I need to know from which bit should I start storing
    local beginOffset = beginBit%8 - 1
    -- Same applies to the end
    local endOffset = endBit - beginOffset + 1

    for num = 1,#range do
        local byte = str:sub( num, num ):byte()
        -- Figure out how far in the bits table we are
        local offset = (num-1)*8 - beginOffset

        -- I won't really go into details of byte to bit conversion, as I lack experience with these sort of operations in order to explain them properly
        for bit=8, 1, -1 do
            -- Some micro optimisation, probably no big performance gain, but might make a change on bigger captures
            local sub = byte - byte_t[bit]
            -- Current output bit
            local pos = offset + bit

            -- If current bit is smaller than byte, subtract bit from it and set the current bit to true.
            -- Otherwise simply set current output bit to false
            if sub >= 0 then
                -- Save only the bits in the capture range
                if pos > 0 and pos < endOffset then
                    bits[pos] = true

                -- But it's still necessary to subtract byte to continue converting it
                byte = sub
                if pos > 0 and pos < endOffset then
                    bits[pos] = false

    return bits

print( unpack( getBits( "Some content string, serving simply testing purpose", 56, 131  ) ) )

Tried my best to comment it, but I don't have much experience with these sort of manipulations and I'm too sleepy right now.

Amazing, thank you. eLunate 5136 — 5y
Coming back to this after 5 months. What about just getting the value as a number of a range of bits? eLunate 5136 — 4y

Answer this question