1/*
2 * Copyright 2012-2019 Aerospike, Inc.
3 *
4 * Portions may be licensed to Aerospike, Inc. under one or more contributor
5 * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
8 * use this file except in compliance with the License. You may obtain a copy of
9 * the License at http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 * License for the specific language governing permissions and limitations under
15 * the License.
16 */
17package aerospike
18
19// Bit operations. Create bit operations used by client operate command.
20// Offset orientation is left-to-right.  Negative offsets are supported.
21// If the offset is negative, the offset starts backwards from end of the bitmap.
22// If an offset is out of bounds, a parameter error will be returned.
23//
24// Nested CDT operations are supported by optional CTX context arguments.  Example:
25// bin = [[0b00000001, 0b01000010],[0b01011010]]
26// Resize first bitmap (in a list of bitmaps) to 3 bytes.
27// BitOperation.resize("bin", 3, BitResizeFlags.DEFAULT, CTX.listIndex(0))
28// bin result = [[0b00000001, 0b01000010, 0b00000000],[0b01011010]]
29//
30const (
31	_CDT_BITWISE_RESIZE   = 0
32	_CDT_BITWISE_INSERT   = 1
33	_CDT_BITWISE_REMOVE   = 2
34	_CDT_BITWISE_SET      = 3
35	_CDT_BITWISE_OR       = 4
36	_CDT_BITWISE_XOR      = 5
37	_CDT_BITWISE_AND      = 6
38	_CDT_BITWISE_NOT      = 7
39	_CDT_BITWISE_LSHIFT   = 8
40	_CDT_BITWISE_RSHIFT   = 9
41	_CDT_BITWISE_ADD      = 10
42	_CDT_BITWISE_SUBTRACT = 11
43	_CDT_BITWISE_SET_INT  = 12
44	_CDT_BITWISE_GET      = 50
45	_CDT_BITWISE_COUNT    = 51
46	_CDT_BITWISE_LSCAN    = 52
47	_CDT_BITWISE_RSCAN    = 53
48	_CDT_BITWISE_GET_INT  = 54
49
50	_CDT_BITWISE_INT_FLAGS_SIGNED = 1
51)
52
53// BitResizeOp creates byte "resize" operation.
54// Server resizes byte[] to byteSize according to resizeFlags (See {@link BitResizeFlags}).
55// Server does not return a value.
56// Example:
57// bin = [0b00000001, 0b01000010]
58// byteSize = 4
59// resizeFlags = 0
60// bin result = [0b00000001, 0b01000010, 0b00000000, 0b00000000]
61func BitResizeOp(policy *BitPolicy, binName string, byteSize int, resizeFlags int, ctx ...*CDTContext) *Operation {
62	return &Operation{
63		opType:   _BIT_MODIFY,
64		ctx:      ctx,
65		binName:  binName,
66		binValue: ListValue{_CDT_BITWISE_RESIZE, IntegerValue(byteSize), IntegerValue(policy.flags), IntegerValue(resizeFlags)},
67		encoder:  newCDTBitwiseEncoder,
68	}
69}
70
71// BitInsertOp creates byte "insert" operation.
72// Server inserts value bytes into byte[] bin at byteOffset.
73// Server does not return a value.
74// Example:
75// bin = [0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]
76// byteOffset = 1
77// value = [0b11111111, 0b11000111]
78// bin result = [0b00000001, 0b11111111, 0b11000111, 0b01000010, 0b00000011, 0b00000100, 0b00000101]
79func BitInsertOp(policy *BitPolicy, binName string, byteOffset int, value []byte, ctx ...*CDTContext) *Operation {
80	// Packer packer = new Packer();
81	// init(packer, ctx, INSERT, 3)
82	// packer.packInt(byteOffset)
83	// packer.packBytes(value)
84	// packer.packInt(policy.flags)
85	// return newOperation(_BIT_MODIFY, binName, Value.get(packer.toByteArray()))
86
87	return &Operation{
88		opType:   _BIT_MODIFY,
89		ctx:      ctx,
90		binName:  binName,
91		binValue: ListValue{_CDT_BITWISE_INSERT, IntegerValue(byteOffset), BytesValue(value), IntegerValue(policy.flags)},
92		encoder:  newCDTBitwiseEncoder,
93	}
94}
95
96// BitRemoveOp creates byte "remove" operation.
97// Server removes bytes from byte[] bin at byteOffset for byteSize.
98// Server does not return a value.
99// Example:
100// bin = [0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]
101// byteOffset = 2
102// byteSize = 3
103// bin result = [0b00000001, 0b01000010]
104func BitRemoveOp(policy *BitPolicy, binName string, byteOffset int, byteSize int, ctx ...*CDTContext) *Operation {
105
106	return &Operation{
107		opType:   _BIT_MODIFY,
108		ctx:      ctx,
109		binName:  binName,
110		binValue: ListValue{_CDT_BITWISE_REMOVE, IntegerValue(byteOffset), IntegerValue(byteSize), IntegerValue(policy.flags)},
111		encoder:  newCDTBitwiseEncoder,
112	}
113}
114
115// BitSetOp creates bit "set" operation.
116// Server sets value on byte[] bin at bitOffset for bitSize.
117// Server does not return a value.
118// Example:
119// bin = [0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]
120// bitOffset = 13
121// bitSize = 3
122// value = [0b11100000]
123// bin result = [0b00000001, 0b01000111, 0b00000011, 0b00000100, 0b00000101]
124func BitSetOp(policy *BitPolicy, binName string, bitOffset int, bitSize int, value []byte, ctx ...*CDTContext) *Operation {
125	return &Operation{
126		opType:   _BIT_MODIFY,
127		ctx:      ctx,
128		binName:  binName,
129		binValue: ListValue{_CDT_BITWISE_SET, IntegerValue(bitOffset), IntegerValue(bitSize), BytesValue(value), IntegerValue(policy.flags)},
130		encoder:  newCDTBitwiseEncoder,
131	}
132}
133
134// BitOrOp creates bit "or" operation.
135// Server performs bitwise "or" on value and byte[] bin at bitOffset for bitSize.
136// Server does not return a value.
137// Example:
138// bin = [0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]
139// bitOffset = 17
140// bitSize = 6
141// value = [0b10101000]
142// bin result = [0b00000001, 0b01000010, 0b01010111, 0b00000100, 0b00000101]
143func BitOrOp(policy *BitPolicy, binName string, bitOffset int, bitSize int, value []byte, ctx ...*CDTContext) *Operation {
144	return &Operation{
145		opType:   _BIT_MODIFY,
146		ctx:      ctx,
147		binName:  binName,
148		binValue: ListValue{_CDT_BITWISE_OR, IntegerValue(bitOffset), IntegerValue(bitSize), BytesValue(value), IntegerValue(policy.flags)},
149		encoder:  newCDTBitwiseEncoder,
150	}
151}
152
153// BitXorOp creates bit "exclusive or" operation.
154// Server performs bitwise "xor" on value and byte[] bin at bitOffset for bitSize.
155// Server does not return a value.
156// Example:
157// bin = [0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]
158// bitOffset = 17
159// bitSize = 6
160// value = [0b10101100]
161// bin result = [0b00000001, 0b01000010, 0b01010101, 0b00000100, 0b00000101]
162func BitXorOp(policy *BitPolicy, binName string, bitOffset int, bitSize int, value []byte, ctx ...*CDTContext) *Operation {
163	return &Operation{
164		opType:   _BIT_MODIFY,
165		ctx:      ctx,
166		binName:  binName,
167		binValue: ListValue{_CDT_BITWISE_XOR, IntegerValue(bitOffset), IntegerValue(bitSize), BytesValue(value), IntegerValue(policy.flags)},
168		encoder:  newCDTBitwiseEncoder,
169	}
170}
171
172// BitAndOp creates bit "and" operation.
173// Server performs bitwise "and" on value and byte[] bin at bitOffset for bitSize.
174// Server does not return a value.
175// Example:
176// bin = [0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]
177// bitOffset = 23
178// bitSize = 9
179// value = [0b00111100, 0b10000000]
180// bin result = [0b00000001, 0b01000010, 0b00000010, 0b00000000, 0b00000101]
181func BitAndOp(policy *BitPolicy, binName string, bitOffset int, bitSize int, value []byte, ctx ...*CDTContext) *Operation {
182	return &Operation{
183		opType:   _BIT_MODIFY,
184		ctx:      ctx,
185		binName:  binName,
186		binValue: ListValue{_CDT_BITWISE_AND, IntegerValue(bitOffset), IntegerValue(bitSize), BytesValue(value), IntegerValue(policy.flags)},
187		encoder:  newCDTBitwiseEncoder,
188	}
189}
190
191// BitNotOp creates bit "not" operation.
192// Server negates byte[] bin starting at bitOffset for bitSize.
193// Server does not return a value.
194// Example:
195// bin = [0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]
196// bitOffset = 25
197// bitSize = 6
198// bin result = [0b00000001, 0b01000010, 0b00000011, 0b01111010, 0b00000101]
199func BitNotOp(policy *BitPolicy, binName string, bitOffset int, bitSize int, ctx ...*CDTContext) *Operation {
200	return &Operation{
201		opType:   _BIT_MODIFY,
202		ctx:      ctx,
203		binName:  binName,
204		binValue: ListValue{_CDT_BITWISE_NOT, IntegerValue(bitOffset), IntegerValue(bitSize), IntegerValue(policy.flags)},
205		encoder:  newCDTBitwiseEncoder,
206	}
207}
208
209// BitLShiftOp creates bit "left shift" operation.
210// Server shifts left byte[] bin starting at bitOffset for bitSize.
211// Server does not return a value.
212// Example:
213// bin = [0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]
214// bitOffset = 32
215// bitSize = 8
216// shift = 3
217// bin result = [0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00101000]
218func BitLShiftOp(policy *BitPolicy, binName string, bitOffset int, bitSize int, shift int, ctx ...*CDTContext) *Operation {
219	return &Operation{
220		opType:   _BIT_MODIFY,
221		ctx:      ctx,
222		binName:  binName,
223		binValue: ListValue{_CDT_BITWISE_LSHIFT, IntegerValue(bitOffset), IntegerValue(bitSize), IntegerValue(shift), IntegerValue(policy.flags)},
224		encoder:  newCDTBitwiseEncoder,
225	}
226}
227
228// BitRShiftOp creates bit "right shift" operation.
229// Server shifts right byte[] bin starting at bitOffset for bitSize.
230// Server does not return a value.
231// Example:
232// bin = [0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]
233// bitOffset = 0
234// bitSize = 9
235// shift = 1
236// bin result = [0b00000000, 0b11000010, 0b00000011, 0b00000100, 0b00000101]
237func BitRShiftOp(policy *BitPolicy, binName string, bitOffset int, bitSize int, shift int, ctx ...*CDTContext) *Operation {
238	return &Operation{
239		opType:   _BIT_MODIFY,
240		ctx:      ctx,
241		binName:  binName,
242		binValue: ListValue{_CDT_BITWISE_RSHIFT, IntegerValue(bitOffset), IntegerValue(bitSize), IntegerValue(shift), IntegerValue(policy.flags)},
243		encoder:  newCDTBitwiseEncoder,
244	}
245}
246
247// BitAddOp creates bit "add" operation.
248// Server adds value to byte[] bin starting at bitOffset for bitSize. BitSize must be <= 64.
249// Signed indicates if bits should be treated as a signed number.
250// If add overflows/underflows, {@link BitOverflowAction} is used.
251// Server does not return a value.
252// Example:
253// bin = [0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]
254// bitOffset = 24
255// bitSize = 16
256// value = 128
257// signed = false
258// bin result = [0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b10000101]
259func BitAddOp(
260	policy *BitPolicy,
261	binName string,
262	bitOffset int,
263	bitSize int,
264	value int64,
265	signed bool,
266	action BitOverflowAction,
267	ctx ...*CDTContext,
268) *Operation {
269	// return createMathOperation(ADD, policy, binName, ctx, bitOffset, bitSize, value, signed, action)
270
271	actionFlags := action
272	if signed {
273		actionFlags |= _CDT_BITWISE_INT_FLAGS_SIGNED
274	}
275	return &Operation{
276		opType:   _BIT_MODIFY,
277		ctx:      ctx,
278		binName:  binName,
279		binValue: ListValue{_CDT_BITWISE_ADD, IntegerValue(bitOffset), IntegerValue(bitSize), IntegerValue(value), IntegerValue(policy.flags), IntegerValue(actionFlags)},
280		encoder:  newCDTBitwiseEncoder,
281	}
282}
283
284// BitSubtractOp creates bit "subtract" operation.
285// Server subtracts value from byte[] bin starting at bitOffset for bitSize. BitSize must be <= 64.
286// Signed indicates if bits should be treated as a signed number.
287// If add overflows/underflows, {@link BitOverflowAction} is used.
288// Server does not return a value.
289// Example:
290// bin = [0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]
291// bitOffset = 24
292// bitSize = 16
293// value = 128
294// signed = false
295// bin result = [0b00000001, 0b01000010, 0b00000011, 0b0000011, 0b10000101]
296func BitSubtractOp(
297	policy *BitPolicy,
298	binName string,
299	bitOffset int,
300	bitSize int,
301	value int64,
302	signed bool,
303	action BitOverflowAction,
304	ctx ...*CDTContext,
305) *Operation {
306	// return createMathOperation(SUBTRACT, policy, binName, ctx, bitOffset, bitSize, value, signed, action)
307
308	actionFlags := action
309	if signed {
310		actionFlags |= _CDT_BITWISE_INT_FLAGS_SIGNED
311	}
312	return &Operation{
313		opType:   _BIT_MODIFY,
314		ctx:      ctx,
315		binName:  binName,
316		binValue: ListValue{_CDT_BITWISE_SUBTRACT, IntegerValue(bitOffset), IntegerValue(bitSize), IntegerValue(value), IntegerValue(policy.flags), IntegerValue(actionFlags)},
317		encoder:  newCDTBitwiseEncoder,
318	}
319}
320
321// BitSetIntOp creates bit "setInt" operation.
322// Server sets value to byte[] bin starting at bitOffset for bitSize. Size must be <= 64.
323// Server does not return a value.
324// Example:
325// bin = [0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]
326// bitOffset = 1
327// bitSize = 8
328// value = 127
329// bin result = [0b00111111, 0b11000010, 0b00000011, 0b0000100, 0b00000101]
330func BitSetIntOp(policy *BitPolicy, binName string, bitOffset int, bitSize int, value int64, ctx ...*CDTContext) *Operation {
331	// Packer packer = new Packer();
332	// init(packer, ctx, SET_INT, 4)
333	// packer.packInt(bitOffset)
334	// packer.packInt(bitSize)
335	// packer.packLong(value)
336	// packer.packInt(policy.flags)
337	// return newOperation(_BIT_MODIFY, binName, Value.get(packer.toByteArray()))
338	return &Operation{
339		opType:   _BIT_MODIFY,
340		ctx:      ctx,
341		binName:  binName,
342		binValue: ListValue{_CDT_BITWISE_SET_INT, IntegerValue(bitOffset), IntegerValue(bitSize), IntegerValue(value), IntegerValue(policy.flags)},
343		encoder:  newCDTBitwiseEncoder,
344	}
345}
346
347// BitGetOp creates bit "get" operation.
348// Server returns bits from byte[] bin starting at bitOffset for bitSize.
349// Example:
350// bin = [0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]
351// bitOffset = 9
352// bitSize = 5
353// returns [0b1000000]
354func BitGetOp(binName string, bitOffset int, bitSize int, ctx ...*CDTContext) *Operation {
355	return &Operation{
356		opType:   _BIT_READ,
357		ctx:      ctx,
358		binName:  binName,
359		binValue: ListValue{_CDT_BITWISE_GET, IntegerValue(bitOffset), IntegerValue(bitSize)},
360		encoder:  newCDTBitwiseEncoder,
361	}
362}
363
364// BitCountOp creates bit "count" operation.
365// Server returns integer count of set bits from byte[] bin starting at bitOffset for bitSize.
366// Example:
367// bin = [0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]
368// bitOffset = 20
369// bitSize = 4
370// returns 2
371func BitCountOp(binName string, bitOffset int, bitSize int, ctx ...*CDTContext) *Operation {
372	return &Operation{
373		opType:   _BIT_READ,
374		ctx:      ctx,
375		binName:  binName,
376		binValue: ListValue{_CDT_BITWISE_COUNT, IntegerValue(bitOffset), IntegerValue(bitSize)},
377		encoder:  newCDTBitwiseEncoder,
378	}
379}
380
381// BitLScanOp creates bit "left scan" operation.
382// Server returns integer bit offset of the first specified value bit in byte[] bin
383// starting at bitOffset for bitSize.
384// Example:
385// bin = [0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]
386// bitOffset = 24
387// bitSize = 8
388// value = true
389// returns 5
390func BitLScanOp(binName string, bitOffset int, bitSize int, value bool, ctx ...*CDTContext) *Operation {
391	return &Operation{
392		opType:   _BIT_READ,
393		ctx:      ctx,
394		binName:  binName,
395		binValue: ListValue{_CDT_BITWISE_LSCAN, IntegerValue(bitOffset), IntegerValue(bitSize), _BoolValue(value)},
396		encoder:  newCDTBitwiseEncoder,
397	}
398}
399
400// BitRScanOp creates bit "right scan" operation.
401// Server returns integer bit offset of the last specified value bit in byte[] bin
402// starting at bitOffset for bitSize.
403// Example:
404// bin = [0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]
405// bitOffset = 32
406// bitSize = 8
407// value = true
408// returns 7
409func BitRScanOp(binName string, bitOffset int, bitSize int, value bool, ctx ...*CDTContext) *Operation {
410	return &Operation{
411		opType:   _BIT_READ,
412		ctx:      ctx,
413		binName:  binName,
414		binValue: ListValue{_CDT_BITWISE_RSCAN, IntegerValue(bitOffset), IntegerValue(bitSize), _BoolValue(value)},
415		encoder:  newCDTBitwiseEncoder,
416	}
417}
418
419// BitGetIntOp creates bit "get integer" operation.
420// Server returns integer from byte[] bin starting at bitOffset for bitSize.
421// Signed indicates if bits should be treated as a signed number.
422// Example:
423// bin = [0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]
424// bitOffset = 8
425// bitSize = 16
426// signed = false
427// returns 16899
428func BitGetIntOp(binName string, bitOffset int, bitSize int, signed bool, ctx ...*CDTContext) *Operation {
429	binValue := ListValue{_CDT_BITWISE_GET_INT, IntegerValue(bitOffset), IntegerValue(bitSize)}
430	if signed {
431		binValue = append(binValue, IntegerValue(_CDT_BITWISE_INT_FLAGS_SIGNED))
432	}
433	return &Operation{
434		opType:   _BIT_READ,
435		ctx:      ctx,
436		binName:  binName,
437		binValue: binValue,
438		encoder:  newCDTBitwiseEncoder,
439	}
440}
441
442func newCDTBitwiseEncoder(op *Operation, packer BufferEx) (int, error) {
443	params := op.binValue.(ListValue)
444	opType := params[0].(int)
445	if len(op.binValue.(ListValue)) > 1 {
446		return packCDTBitIfcParamsAsArray(packer, int16(opType), op.ctx, params[1:])
447	}
448	return packCDTBitIfcParamsAsArray(packer, int16(opType), op.ctx, nil)
449}
450
451func packCDTBitIfcParamsAsArray(packer BufferEx, opType int16, ctx []*CDTContext, params ListValue) (int, error) {
452	return packCDTBitIfcVarParamsAsArray(packer, opType, ctx, []interface{}(params)...)
453}
454
455func packCDTBitIfcVarParamsAsArray(packer BufferEx, opType int16, ctx []*CDTContext, params ...interface{}) (int, error) {
456	size := 0
457	n := 0
458	var err error
459	if len(ctx) > 0 {
460		if n, err = packArrayBegin(packer, 3); err != nil {
461			return size + n, err
462		}
463		size += n
464
465		if n, err = packAInt64(packer, 0xff); err != nil {
466			return size + n, err
467		}
468		size += n
469
470		if n, err = packArrayBegin(packer, len(ctx)*2); err != nil {
471			return size + n, err
472		}
473		size += n
474
475		for _, c := range ctx {
476			if n, err = packAInt64(packer, int64(c.id)); err != nil {
477				return size + n, err
478			}
479			size += n
480
481			if n, err = c.value.pack(packer); err != nil {
482				return size + n, err
483			}
484			size += n
485		}
486	}
487
488	if n, err = packArrayBegin(packer, len(params)+1); err != nil {
489		return size + n, err
490	}
491	size += n
492
493	if n, err = packAInt(packer, int(opType)); err != nil {
494		return size + n, err
495	}
496	size += n
497
498	if len(params) > 0 {
499		for i := range params {
500			if n, err = packObject(packer, params[i], false); err != nil {
501				return size + n, err
502			}
503			size += n
504		}
505	}
506
507	return size, nil
508}
509