using System.Buffers.Text;
using System.Diagnostics;
using Garnet.common;
using Garnet.server;
using Tsavorite.core;
namespace GarnetRoaringBitmap
{
internal static class RoaringBitmapArgs
{
public static bool TryParseUInt32(ReadOnlySpan<byte> raw, out uint value)
{
if (Utf8Parser.TryParse(raw, out long signed, out int consumed) && consumed == raw.Length && signed >= 0 && signed <= uint.MaxValue)
{
value = (uint)signed;
return true;
}
value = 0;
return false;
}
public static bool TryParseBit(ReadOnlySpan<byte> raw, out bool bit)
{
if (raw.Length == 1 && raw[0] == (byte)'0') { bit = false; return true; }
if (raw.Length == 1 && raw[0] == (byte)'1') { bit = true; return true; }
bit = false;
return false;
}
}
public sealed class RSetBit : CustomObjectFunctions
{
private static ReadOnlySpan<byte> ErrOffset => "ERR bit offset is not an unsigned 32-bit integer"u8;
private static ReadOnlySpan<byte> ErrValue => "ERR bit value must be 0 or 1"u8;
public override bool NeedInitialUpdate(scoped ReadOnlySpan<byte> key, ref ObjectInput input, ref RespMemoryWriter writer)
{
var validation = input;
var offset = 0;
var offsetArg = GetNextArg(ref validation, ref offset);
var bitArg = GetNextArg(ref validation, ref offset);
if (!RoaringBitmapArgs.TryParseUInt32(offsetArg, out _))
{
writer.WriteError(ErrOffset);
return false;
}
if (!RoaringBitmapArgs.TryParseBit(bitArg, out _))
{
writer.WriteError(ErrValue);
return false;
}
return true;
}
public override bool Updater(ReadOnlySpan<byte> key, ref ObjectInput input, IGarnetObject value, ref RespMemoryWriter writer, ref RMWInfo rmwInfo)
{
Debug.Assert(value is RoaringBitmapObject);
var rb = (RoaringBitmapObject)value;
var offset = 0;
var offsetArg = GetNextArg(ref input, ref offset);
var bitArg = GetNextArg(ref input, ref offset);
if (!RoaringBitmapArgs.TryParseUInt32(offsetArg, out var bitOffset))
return AbortWithErrorMessage(ref writer, ErrOffset);
if (!RoaringBitmapArgs.TryParseBit(bitArg, out var bit))
return AbortWithErrorMessage(ref writer, ErrValue);
var previous = rb.SetBit(bitOffset, bit);
writer.WriteInt32(previous);
return true;
}
}
public sealed class RGetBit : CustomObjectFunctions
{
private static ReadOnlySpan<byte> ErrOffset => "ERR bit offset is not an unsigned 32-bit integer"u8;
public override bool Reader(ReadOnlySpan<byte> key, ref ObjectInput input, IGarnetObject value, ref RespMemoryWriter writer, ref ReadInfo readInfo)
{
Debug.Assert(value is RoaringBitmapObject);
var rb = (RoaringBitmapObject)value;
var offsetArg = GetFirstArg(ref input);
if (!RoaringBitmapArgs.TryParseUInt32(offsetArg, out var bitOffset))
return AbortWithErrorMessage(ref writer, ErrOffset);
writer.WriteInt32(rb.GetBit(bitOffset));
return true;
}
public override void NotFound(ReadOnlySpan<byte> key, ref ObjectInput input, ref RespMemoryWriter writer)
{
var offsetArg = GetFirstArg(ref input);
if (!RoaringBitmapArgs.TryParseUInt32(offsetArg, out _))
writer.WriteError(ErrOffset);
else
writer.WriteInt32(0);
}
}
public sealed class RBitCount : CustomObjectFunctions
{
public override bool Reader(ReadOnlySpan<byte> key, ref ObjectInput input, IGarnetObject value, ref RespMemoryWriter writer, ref ReadInfo readInfo)
{
Debug.Assert(value is RoaringBitmapObject);
var rb = (RoaringBitmapObject)value;
writer.WriteInt64(rb.BitCount());
return true;
}
public override void NotFound(ReadOnlySpan<byte> key, ref ObjectInput input, ref RespMemoryWriter writer)
=> writer.WriteInt64(0);
}
public sealed class RBitPos : CustomObjectFunctions
{
private static ReadOnlySpan<byte> ErrBit => "ERR bit must be 0 or 1"u8;
private static ReadOnlySpan<byte> ErrFrom => "ERR from offset is not an unsigned 32-bit integer"u8;
public override bool Reader(ReadOnlySpan<byte> key, ref ObjectInput input, IGarnetObject value, ref RespMemoryWriter writer, ref ReadInfo readInfo)
{
Debug.Assert(value is RoaringBitmapObject);
var rb = (RoaringBitmapObject)value;
if (!TryParseArgs(ref input, ref writer, out var bit, out var from))
return true;
var pos = rb.BitPos(bit ? 1 : 0, from);
writer.WriteInt64(pos);
return true;
}
public override void NotFound(ReadOnlySpan<byte> key, ref ObjectInput input, ref RespMemoryWriter writer)
{
if (!TryParseArgs(ref input, ref writer, out var bit, out var from))
return;
writer.WriteInt64(bit ? -1 : from);
}
private static bool TryParseArgs(ref ObjectInput input, ref RespMemoryWriter writer, out bool bit, out uint from)
{
from = 0;
var offset = 0;
var bitArg = GetNextArg(ref input, ref offset);
if (!RoaringBitmapArgs.TryParseBit(bitArg, out bit))
{
writer.WriteError(ErrBit);
return false;
}
if (input.parseState.Count > offset)
{
var fromArg = GetNextArg(ref input, ref offset);
if (!RoaringBitmapArgs.TryParseUInt32(fromArg, out from))
{
writer.WriteError(ErrFrom);
return false;
}
}
return true;
}
}
}