1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9// 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,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17package array
18
19import (
20	"bytes"
21	"fmt"
22	"strings"
23	"unsafe"
24
25	"github.com/apache/arrow/go/v6/arrow"
26)
27
28// A type which represents an immutable sequence of variable-length binary strings.
29type Binary struct {
30	array
31	valueOffsets []int32
32	valueBytes   []byte
33}
34
35// NewBinaryData constructs a new Binary array from data.
36func NewBinaryData(data *Data) *Binary {
37	a := &Binary{}
38	a.refCount = 1
39	a.setData(data)
40	return a
41}
42
43// Value returns the slice at index i. This value should not be mutated.
44func (a *Binary) Value(i int) []byte {
45	if i < 0 || i >= a.array.data.length {
46		panic("arrow/array: index out of range")
47	}
48	idx := a.array.data.offset + i
49	return a.valueBytes[a.valueOffsets[idx]:a.valueOffsets[idx+1]]
50}
51
52// ValueString returns the string at index i without performing additional allocations.
53// The string is only valid for the lifetime of the Binary array.
54func (a *Binary) ValueString(i int) string {
55	b := a.Value(i)
56	return *(*string)(unsafe.Pointer(&b))
57}
58
59func (a *Binary) ValueOffset(i int) int {
60	if i < 0 || i >= a.array.data.length {
61		panic("arrow/array: index out of range")
62	}
63	return int(a.valueOffsets[a.array.data.offset+i])
64}
65
66func (a *Binary) ValueLen(i int) int {
67	if i < 0 || i >= a.array.data.length {
68		panic("arrow/array: index out of range")
69	}
70	beg := a.array.data.offset + i
71	return int(a.valueOffsets[beg+1] - a.valueOffsets[beg])
72}
73
74func (a *Binary) ValueOffsets() []int32 {
75	beg := a.array.data.offset
76	end := beg + a.array.data.length + 1
77	return a.valueOffsets[beg:end]
78}
79
80func (a *Binary) ValueBytes() []byte {
81	beg := a.array.data.offset
82	end := beg + a.array.data.length
83	return a.valueBytes[a.valueOffsets[beg]:a.valueOffsets[end]]
84}
85
86func (a *Binary) String() string {
87	o := new(strings.Builder)
88	o.WriteString("[")
89	for i := 0; i < a.Len(); i++ {
90		if i > 0 {
91			o.WriteString(" ")
92		}
93		switch {
94		case a.IsNull(i):
95			o.WriteString("(null)")
96		default:
97			fmt.Fprintf(o, "%q", a.ValueString(i))
98		}
99	}
100	o.WriteString("]")
101	return o.String()
102}
103
104func (a *Binary) setData(data *Data) {
105	if len(data.buffers) != 3 {
106		panic("len(data.buffers) != 3")
107	}
108
109	a.array.setData(data)
110
111	if valueData := data.buffers[2]; valueData != nil {
112		a.valueBytes = valueData.Bytes()
113	}
114
115	if valueOffsets := data.buffers[1]; valueOffsets != nil {
116		a.valueOffsets = arrow.Int32Traits.CastFromBytes(valueOffsets.Bytes())
117	}
118}
119
120func arrayEqualBinary(left, right *Binary) bool {
121	for i := 0; i < left.Len(); i++ {
122		if left.IsNull(i) {
123			continue
124		}
125		if !bytes.Equal(left.Value(i), right.Value(i)) {
126			return false
127		}
128	}
129	return true
130}
131
132var (
133	_ Interface = (*Binary)(nil)
134)
135