unit ZXing.Common.BitArrayImplementation; interface uses ZXing.Common.BitArray; function NewBitArray:IBitArray; overload; function NewBitArray(const Size: Integer):IBitArray; overload; implementation uses System.SysUtils, ZXing.Common.Detector.MathUtils, Math; type /// /// A simple, fast array of bits, represented compactly by an array of ints internally. /// TBitArrayImplementation = class(TInterfacedObject, IBitArray) strict private _lookup: TArray; Fbits: TArray; Fsize: Integer; procedure InitLookup(); function GetBit(i: Integer): Boolean; procedure SetBit(i: Integer; Value: Boolean); function makeArray(Size: Integer): TArray; procedure ensureCapacity(size: Integer); function numberOfTrailingZeros(num: Integer): Integer; function GetBits: TArray; private constructor Create(); overload; constructor Create(const Size: Integer); overload; public function Size: Integer; function SizeInBytes: Integer; property Self[i: Integer]: Boolean read GetBit write SetBit; default; property Bits: TArray read Fbits; destructor Destroy; override; function getNextSet(from: Integer): Integer; function getNextUnset(from: Integer): Integer; procedure setBulk(i, newBits: Integer); procedure setRange(start, ending: Integer); procedure appendBit(bit: Boolean); procedure Reverse(); procedure clear(); function isRange(start, ending: Integer; const value: Boolean): Boolean; end; constructor TBitArrayImplementation.Create; begin Fsize := 0; SetLength(Fbits, 1); InitLookup(); end; constructor TBitArrayImplementation.Create(const Size: Integer); begin if (Size < 1) then raise EArgumentException.Create('size must be at least 1.'); Fsize := Size; Fbits := makeArray(Size); InitLookup(); end; procedure TBitArrayImplementation.ensureCapacity(size: Integer); var newBits : TArray; begin if (size > TMathUtils.Asr(Length(Fbits), 5)) then begin newBits := makeArray(size); Move(Fbits[0], newBits[0], Length(Fbits) * SizeOf(Fbits[0])); Fbits := newBits; end; end; destructor TBitArrayImplementation.Destroy; begin Fbits := nil; _lookup := nil; inherited; end; function TBitArrayImplementation.GetBit(i: Integer): Boolean; begin Result := ((Fbits[TMathUtils.Asr(i, 5)]) and (1 shl (i and $1F))) <> 0; end; function TBitArrayImplementation.GetBits: TArray; begin result := FBits; end; /// /// Gets the next set. /// /// first bit to check /// index of first bit that is set, starting from the given index, or size if none are set /// at or beyond this given index function TBitArrayImplementation.getNextSet(from: Integer): Integer; var bitsOffset, currentBits : Integer; begin if (from >= Fsize) then Exit(FSize); bitsOffset := TMathUtils.Asr(from, 5); currentBits := Fbits[bitsOffset]; // mask off lesser bits first {$OVERFLOWCHECKS OFF} currentBits := currentBits and -(1 shl (from and $1F)); {$OVERFLOWCHECKS ON} while (currentBits = 0) do begin Inc(bitsOffset); if (bitsOffset = Length(Fbits)) then Exit(FSize); currentBits := Fbits[bitsOffset]; end; Result := (bitsOffset shl 5) + numberOfTrailingZeros(currentBits); Result := Math.Min(Result, FSize); end; /// /// see getNextSet(int) /// /// index to start looking for unset bit /// index of next unset bit, or if none are unset until the end function TBitArrayImplementation.getNextUnset(from: Integer): Integer; var bitsOffset, currentBits: Integer; begin if (from >= Fsize) then begin Result := Fsize; exit; end; bitsOffset := TMathUtils.Asr(from, 5); currentBits := not Fbits[bitsOffset]; // mask off lesser bits first {$OVERFLOWCHECKS OFF} currentBits := currentBits and -(1 shl (from and $1F)); {$OVERFLOWCHECKS ON} while (currentBits = 0) do begin Inc(bitsOffset); if (bitsOffset = Length(Fbits)) then begin Result := Size; exit; end; currentBits := not Fbits[bitsOffset]; end; Result := (bitsOffset shl 5) + numberOfTrailingZeros(currentBits); if (Result > Size) then Result := Size; end; procedure TBitArrayImplementation.InitLookup; begin _lookup := TArray.Create(32, 0, 1, 26, 2, 23, 27, 0, 3, 16, 24, 30, 28, 11, 0, 13, 4, 7, 17, 0, 25, 22, 31, 15, 29, 10, 12, 6, 0, 21, 14, 9, 5, 20, 8, 19, 18); end; function TBitArrayImplementation.makeArray(Size: Integer): TArray; var ar: TArray; begin SetLength(ar, TMathUtils.Asr((Size + 31), 5)); Result := ar; end; function TBitArrayImplementation.numberOfTrailingZeros(num: Integer): Integer; var index: Integer; begin Result := 0; {$OVERFLOWCHECKS OFF} index := (-num and num) mod 37; if (index < 0) then begin index := index * -1; end; if (index >= Low(_lookup)) and (index <= High(_lookup)) then Result := _lookup[index]; {$OVERFLOWCHECKS ON} end; procedure TBitArrayImplementation.Reverse; var newBits: TArray; i, len, oldBitsLen, leftOffset, mask, nextInt, currentInt: Integer; x: Int64; begin SetLength(newBits, Length(Fbits)); // reverse all int's first len := TMathUtils.Asr((Size - 1), 5); oldBitsLen := len + 1; for i := 0 to oldBitsLen - 1 do begin x := Fbits[i]; x := (TMathUtils.Asr(x, 1) and $55555555) or ((x and $55555555) shl 1); x := (TMathUtils.Asr(x, 2) and $33333333) or ((x and $33333333) shl 2); x := (TMathUtils.Asr(x, 4) and $0F0F0F0F) or ((x and $0F0F0F0F) shl 4); x := (TMathUtils.Asr(x, 8) and $00FF00FF) or ((x and $00FF00FF) shl 8); x := (TMathUtils.Asr(x, 16) and $0000FFFF) or ((x and $0000FFFF) shl 16); newBits[len - i] := integer(x) ; end; // now correct the int's if the bit size isn't a multiple of 32 if (Size <> oldBitsLen * 32) then begin leftOffset := oldBitsLen * 32 - Size; mask := 1; for i := 0 to 31 - leftOffset - 1 do begin mask := (mask shl 1) or 1; end; currentInt := TMathUtils.Asr(newBits[0], leftOffset) and mask; for i := 1 to oldBitsLen - 1 do begin nextInt := newBits[i]; currentInt := currentInt or (nextInt shl (32 - leftOffset)); newBits[i - 1] := currentInt; currentInt := TMathUtils.Asr(nextInt, leftOffset) and mask; end; newBits[oldBitsLen - 1] := currentInt; end; Fbits := newBits; end; procedure TBitArrayImplementation.SetBit(i: Integer; Value: Boolean); var index: Integer; begin if (Value) then begin index := TMathUtils.Asr(i, 5); Fbits[index] := Fbits[index] or 1 shl (i and $1F); end; end; function TBitArrayImplementation.Size: Integer; begin result := FSize; end; function TBitArrayImplementation.SizeInBytes: Integer; begin Result := TMathUtils.Asr(Size + 7, 3); end; /// Sets a block of 32 bits, starting at bit i. /// /// /// first bit to set /// /// the new value of the next 32 bits. Note again that the least-significant bit /// corresponds to bit i, the next-least-significant to i+1, and so on. /// procedure TBitArrayImplementation.setBulk(i, newBits: Integer); var r: Integer; begin r := TMathUtils.Asr(i, 5); Fbits[r] := newBits; end; /// /// Sets a range of bits. /// /// start of range, inclusive. /// end of range, exclusive procedure TBitArrayImplementation.setRange(start, ending: Integer); var firstInt, lastInt, mask, i, j : Integer; firstBit, lastBit : Integer; begin if (ending < start) then raise EArgumentException.Create('Start is greater than end'); if (ending = start) then exit; Dec(ending); // will be easier to treat this as the last actually set bit -- inclusive firstInt := TMathUtils.Asr(start, 5); lastInt := TMathUtils.Asr(ending, 5); for i := firstInt to lastInt do begin if (i > firstInt) then firstBit := start else firstBit := $1F; if (i < lastInt) then lastBit := 31 else lastBit := (ending and $1F); if ((firstBit = 0) and (lastBit = 31)) then mask := -1 else begin mask := 0; for j := firstBit to lastBit do begin mask := (mask or (1 shl j)); end; end; bits[i] := (bits[i] or mask); end; end; /// Clears all bits (sets to false). procedure TBitArrayImplementation.Clear; var max, i: Integer; begin max := Length(Fbits); for i := 0 to Pred(max) do begin Fbits[i] := 0; end; end; /// Efficient method to check if a range of bits is set, or not set. /// /// /// start of range, inclusive. /// /// end of range, exclusive /// /// if true, checks that bits in range are set, otherwise checks that they are not set /// /// true iff all bits are set or not set in range, according to value argument /// /// EIllegalArgumentException if end is less than or equal to start function TBitArrayImplementation.isRange(start, ending: Integer; const value: Boolean): Boolean; var firstInt, lastInt, firstBit, lastBit, mask, i, j, temp: Integer; begin if (ending = start) then begin Result := true; // empty range matches exit; end; Dec(ending); // will be easier to treat this as the last actually set bit -- inclusive firstInt := TMathUtils.Asr(start, 5); lastInt := TMathUtils.Asr(ending, 5); for i := firstInt to lastInt do begin if (i > firstInt) then firstBit := 0 else firstBit := (start and $1F); if (i < lastInt) then lastBit := 31 else lastBit := (ending and $1F); if ((firstBit = 0) and (lastBit = 31)) then mask := -1 else begin mask := 0; for j := firstBit to lastBit do mask := mask or (1 shl j); end; // Return false if we're looking for 1s and the masked bits[i] isn't all 1s (that is, // equals the mask, or we're looking for 0s and the masked portion is not all 0s if (Value) then temp := mask else temp := 0; if ((Fbits[i] and mask) <> (temp)) then begin Result := False; exit; end; end; Result := true; end; /// /// Appends the bit. /// /// The bit. procedure TBitArrayImplementation.appendBit(bit: Boolean); var i: Integer; begin ensureCapacity(Fsize + 1); if (bit) then begin i := TMathUtils.Asr(Fsize, 5); bits[i] := (bits[i] or (Fsize and $1F)); end; Dec(Fsize); end; function NewBitArray:IBitArray; begin result := TBitArrayImplementation.Create; end; function NewBitArray(const Size: Integer):IBitArray; begin result := TBitArrayImplementation.Create(size); end; end.