Search Apps Documentation Source Content File Folder Download Copy Actions Download

tick_bitmap.gno

5.55 Kb ยท 165 lines
  1package v1
  2
  3import (
  4	"strconv"
  5
  6	plp "gno.land/p/gnoswap/gnsmath"
  7	u256 "gno.land/p/gnoswap/uint256"
  8	"gno.land/p/nt/ufmt"
  9	pl "gno.land/r/gnoswap/pool"
 10)
 11
 12// bitMask8 is used for efficient modulo 256 operations
 13const bitMask8 = 0xff // 256 - 1
 14
 15// tickBitmapFlipTick flips the state of a tick in the tick bitmap.
 16//
 17// This function toggles the "initialized" state of a tick in the tick bitmap.
 18// It ensures that the tick aligns with the specified tick spacing and then
 19// flips the corresponding bit in the bitmap representation.
 20//
 21// Parameters:
 22//   - tick: int32, the tick index to toggle.
 23//   - tickSpacing: int32, the spacing between valid ticks.
 24//     The tick must align with this spacing.
 25//
 26// Workflow:
 27//  1. Validates that the `tick` aligns with `tickSpacing` using `checkTickSpacing`.
 28//  2. Computes the position of the bit in the tick bitmap:
 29//     - `wordPos`: Determines which word in the bitmap contains the bit.
 30//     - `bitPos`: Identifies the position of the bit within the word.
 31//  3. Creates a bitmask using `Lsh` (Left Shift) to target the bit at `bitPos`.
 32//  4. Toggles (flips) the bit using XOR with the current value of the tick bitmap.
 33//  5. Updates the tick bitmap with the modified word.
 34//
 35// Behavior:
 36//   - If the bit is `0` (uninitialized), it will be flipped to `1` (initialized).
 37//   - If the bit is `1` (initialized), it will be flipped to `0` (uninitialized).
 38//
 39// Example:
 40//
 41//	pool.tickBitmapFlipTick(120, 60)
 42//	// This flips the bit for tick 120 with a tick spacing of 60.
 43//
 44// Notes:
 45//   - The `tick` must be divisible by `tickSpacing`. If not, the function will panic.
 46func tickBitmapFlipTick(
 47	p *pl.Pool,
 48	tick int32,
 49	tickSpacing int32,
 50) {
 51	checkTickSpacing(tick, tickSpacing)
 52	wordPos, bitPos := tickBitmapPosition(tick / tickSpacing)
 53
 54	mask := u256.Zero().Lsh(u256.One(), uint(bitPos))
 55	current := getTickBitmap(p, wordPos)
 56	setTickBitmap(p, wordPos, u256.Zero().Xor(current, mask))
 57}
 58
 59// tickBitmapNextInitializedTickWithInOneWord finds the next initialized tick within
 60// one word of the bitmap.
 61func tickBitmapNextInitializedTickWithInOneWord(
 62	p *pl.Pool,
 63	tick int32,
 64	tickSpacing int32,
 65	lte bool,
 66) (int32, bool) {
 67	compress := tick / tickSpacing
 68	// Round towards negative infinity for negative ticks
 69	if tick < 0 && tick%tickSpacing != 0 {
 70		compress--
 71	}
 72
 73	wordPos, bitPos := getWordAndBitPos(compress, lte)
 74	mask := getMaskBit(uint(bitPos), lte)
 75	masked := u256.Zero().And(getTickBitmap(p, wordPos), mask)
 76	initialized := !masked.IsZero()
 77
 78	nextTick := getNextTick(lte, initialized, compress, bitPos, tickSpacing, masked)
 79	return nextTick, initialized
 80}
 81
 82// getTickBitmap gets the tick bitmap for the given word position
 83// if the tick bitmap is not initialized, initialize it to zero
 84func getTickBitmap(p *pl.Pool, wordPos int16) *u256.Uint {
 85	wordPosStr := strconv.Itoa(int(wordPos))
 86
 87	value, exist := p.TickBitmaps().Get(wordPosStr)
 88	if !exist {
 89		setTickBitmap(p, wordPos, u256.Zero())
 90		value, exist = p.TickBitmaps().Get(wordPosStr)
 91		if !exist {
 92			panic(newErrorWithDetail(
 93				errDataNotFound,
 94				ufmt.Sprintf("failed to initialize tickBitmap(%d)", wordPos),
 95			))
 96		}
 97	}
 98
 99	bitmap, ok := value.(*u256.Uint)
100	if !ok {
101		panic(ufmt.Sprintf("failed to cast tickBitmap to *u256.Uint: %T", value))
102	}
103	return bitmap
104}
105
106// setTickBitmap sets the tick bitmap for the given word position
107func setTickBitmap(p *pl.Pool, wordPos int16, tickBitmap *u256.Uint) {
108	wordPosStr := strconv.Itoa(int(wordPos))
109	p.TickBitmaps().Set(wordPosStr, tickBitmap)
110}
111
112// tickBitmapPosition calculates the word and bit position for a given tick
113func tickBitmapPosition(tick int32) (int16, uint8) {
114	return int16(tick >> 8), uint8(tick) & bitMask8
115}
116
117// getWordAndBitPos gets tick's wordPos and bitPos depending on the swap direction
118func getWordAndBitPos(tick int32, lte bool) (int16, uint8) {
119	if !lte {
120		tick++
121	}
122	return tickBitmapPosition(tick)
123}
124
125// getMaskBit generates a mask based on the provided bit position (bitPos) and a boolean flag (lte).
126// The function constructs a bitmask with a shift depending on the bit position and the boolean value.
127// It either returns the mask or its negation, based on the value of 'lte' (swap direction).
128//
129// NOTE: should always use a newly created `u256.One()` object.
130func getMaskBit(bitPos uint, lte bool) *u256.Uint {
131	if lte {
132		if bitPos == bitMask8 {
133			return u256.Zero().SetAllOne()
134		}
135		return u256.Zero().Sub(u256.Zero().Lsh(u256.One(), bitPos+1), u256.One())
136	}
137	if bitPos == 0 {
138		return u256.Zero().SetAllOne()
139	}
140	return u256.Zero().Not(u256.Zero().Sub(u256.Zero().Lsh(u256.One(), bitPos), u256.One()))
141}
142
143// getNextTick gets the next tick depending on the initialized state and the swap direction
144func getNextTick(lte, initialized bool, compress int32, bitPos uint8, tickSpacing int32, masked *u256.Uint) int32 {
145	if initialized {
146		return getTickIfInitialized(compress, tickSpacing, bitPos, masked, lte)
147	}
148	return getTickIfNotInitialized(compress, tickSpacing, bitPos, lte)
149}
150
151// getTickIfInitialized gets the next tick if the tick bitmap is initialized
152func getTickIfInitialized(compress, tickSpacing int32, bitPos uint8, masked *u256.Uint, lte bool) int32 {
153	if lte {
154		return (compress - int32(bitPos-plp.BitMathMostSignificantBit(masked))) * tickSpacing
155	}
156	return (compress + 1 + int32(plp.BitMathLeastSignificantBit(masked)-bitPos)) * tickSpacing
157}
158
159// getTickIfNotInitialized gets the next tick if the tick bitmap is not initialized
160func getTickIfNotInitialized(compress, tickSpacing int32, bitPos uint8, lte bool) int32 {
161	if lte {
162		return (compress - int32(bitPos)) * tickSpacing
163	}
164	return (compress + 1 + int32(bitMask8-bitPos)) * tickSpacing
165}