1 // Copyright 2018 The Draco Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 
16 #ifndef DRACO_COMPRESSION_ATTRIBUTES_POINT_D_VECTOR_H_
17 #define DRACO_COMPRESSION_ATTRIBUTES_POINT_D_VECTOR_H_
18 
19 #include <cstring>
20 #include <memory>
21 #include <vector>
22 #include "draco/core/macros.h"
23 
24 namespace draco {
25 
26 // The main class of this file is PointDVector providing an interface similar to
27 // std::vector<PointD> for arbitrary number of dimensions (without a template
28 // argument). PointDVectorIterator is a random access iterator, which allows for
29 // compatibility with existing algorithms. PseudoPointD provides for a view on
30 // the individual items in a contiguous block of memory, which is compatible
31 // with the swap function and is returned by a dereference of
32 // PointDVectorIterator. Swap functions provide for compatibility/specialization
33 // that allows these classes to work with currently utilized STL functions.
34 
35 // This class allows for swap functionality from the RandomIterator
36 // It seems problematic to bring this inside PointDVector due to templating.
37 template <typename internal_t>
38 class PseudoPointD {
39  public:
PseudoPointD(internal_t * mem,internal_t dimension)40   PseudoPointD(internal_t *mem, internal_t dimension)
41       : mem_(mem), dimension_(dimension) {}
42 
43   // Specifically copies referenced memory
swap(PseudoPointD & other)44   void swap(PseudoPointD &other) noexcept {
45     for (internal_t dim = 0; dim < dimension_; dim += 1)
46       std::swap(mem_[dim], other.mem_[dim]);
47   }
48 
PseudoPointD(const PseudoPointD & other)49   PseudoPointD(const PseudoPointD &other)
50       : mem_(other.mem_), dimension_(other.dimension_) {}
51 
52   const internal_t &operator[](const size_t &n) const {
53     DRACO_DCHECK_LT(n, dimension_);
54     return mem_[n];
55   }
56   internal_t &operator[](const size_t &n) {
57     DRACO_DCHECK_LT(n, dimension_);
58     return mem_[n];
59   }
60 
61   bool operator==(const PseudoPointD &other) const {
62     for (auto dim = 0; dim < dimension_; dim += 1)
63       if (mem_[dim] != other.mem_[dim])
64         return false;
65     return true;
66   }
67   bool operator!=(const PseudoPointD &other) const {
68     return !this->operator==(other);
69   }
70 
71  private:
72   internal_t *const mem_;
73   const internal_t dimension_;
74 };
75 
76 // It seems problematic to bring this inside PointDVector due to templating.
77 template <typename internal_t>
swap(draco::PseudoPointD<internal_t> && a,draco::PseudoPointD<internal_t> && b)78 void swap(draco::PseudoPointD<internal_t> &&a,
79           draco::PseudoPointD<internal_t> &&b) noexcept {
80   a.swap(b);
81 };
82 template <typename internal_t>
swap(draco::PseudoPointD<internal_t> & a,draco::PseudoPointD<internal_t> & b)83 void swap(draco::PseudoPointD<internal_t> &a,
84           draco::PseudoPointD<internal_t> &b) noexcept {
85   a.swap(b);
86 };
87 
88 template <typename internal_t>
89 class PointDVector {
90  public:
PointDVector(const uint32_t n_items,const uint32_t dimensionality)91   PointDVector(const uint32_t n_items, const uint32_t dimensionality)
92       : n_items_(n_items),
93         dimensionality_(dimensionality),
94         item_size_bytes_(dimensionality * sizeof(internal_t)),
95         data_(n_items * dimensionality),
96         data0_(data_.data()) {}
97   // random access iterator
98   class PointDVectorIterator
99       : public std::iterator<std::random_access_iterator_tag, size_t, size_t> {
100     friend class PointDVector;
101 
102    public:
103     // std::iter_swap is called inside of std::partition and needs this
104     // specialized support
105     PseudoPointD<internal_t> operator*() const {
106       return PseudoPointD<internal_t>(vec_->data0_ + item_ * dimensionality_,
107                                       dimensionality_);
108     }
109     const PointDVectorIterator &operator++() {
110       item_ += 1;
111       return *this;
112     }
113     const PointDVectorIterator &operator--() {
114       item_ -= 1;
115       return *this;
116     }
117     PointDVectorIterator operator++(int32_t) {
118       PointDVectorIterator copy(*this);
119       item_ += 1;
120       return copy;
121     }
122     PointDVectorIterator operator--(int32_t) {
123       PointDVectorIterator copy(*this);
124       item_ -= 1;
125       return copy;
126     }
127     PointDVectorIterator &operator=(const PointDVectorIterator &other) {
128       this->item_ = other.item_;
129       return *this;
130     }
131 
132     bool operator==(const PointDVectorIterator &ref) const {
133       return item_ == ref.item_;
134     }
135     bool operator!=(const PointDVectorIterator &ref) const {
136       return item_ != ref.item_;
137     }
138     bool operator<(const PointDVectorIterator &ref) const {
139       return item_ < ref.item_;
140     }
141     bool operator>(const PointDVectorIterator &ref) const {
142       return item_ > ref.item_;
143     }
144     bool operator<=(const PointDVectorIterator &ref) const {
145       return item_ <= ref.item_;
146     }
147     bool operator>=(const PointDVectorIterator &ref) const {
148       return item_ >= ref.item_;
149     }
150 
151     PointDVectorIterator operator+(const int32_t &add) const {
152       PointDVectorIterator copy(vec_, item_ + add);
153       return copy;
154     }
155     PointDVectorIterator &operator+=(const int32_t &add) {
156       item_ += add;
157       return *this;
158     }
159     PointDVectorIterator operator-(const int32_t &sub) const {
160       PointDVectorIterator copy(vec_, item_ - sub);
161       return copy;
162     }
163     size_t operator-(const PointDVectorIterator &sub) const {
164       return (item_ - sub.item_);
165     }
166 
167     PointDVectorIterator &operator-=(const int32_t &sub) {
168       item_ -= sub;
169       return *this;
170     }
171 
172     internal_t *operator[](const size_t &n) const {
173       return vec_->data0_ + (item_ + n) * dimensionality_;
174     }
175 
176    protected:
PointDVectorIterator(PointDVector * vec,size_t start_item)177     explicit PointDVectorIterator(PointDVector *vec, size_t start_item)
178         : item_(start_item), vec_(vec), dimensionality_(vec->dimensionality_) {}
179 
180    private:
181     size_t item_;  // this counts the item that should be referenced.
182     PointDVector *const vec_;        // the thing that we're iterating on
183     const uint32_t dimensionality_;  // local copy from vec_
184   };
185 
begin()186   PointDVectorIterator begin() { return PointDVectorIterator(this, 0); }
end()187   PointDVectorIterator end() { return PointDVectorIterator(this, n_items_); }
188 
189   // operator[] allows for unprotected user-side usage of operator[] on the
190   // return value AS IF it were a natively indexable type like Point3*
191   internal_t *operator[](const uint32_t index) {
192     DRACO_DCHECK_LT(index, n_items_);
193     return data0_ + index * dimensionality_;
194   }
195   const internal_t *operator[](const uint32_t index) const {
196     DRACO_DCHECK_LT(index, n_items_);
197     return data0_ + index * dimensionality_;
198   }
199 
size()200   uint32_t size() const { return n_items_; }
GetBufferSize()201   size_t GetBufferSize() const { return data_.size(); }
202 
203   // copy a single contiguous 'item' from one PointDVector into this one.
CopyItem(const PointDVector & source,const internal_t source_index,const internal_t destination_index)204   void CopyItem(const PointDVector &source, const internal_t source_index,
205                 const internal_t destination_index) {
206     DRACO_DCHECK(&source != this ||
207                  (&source == this && source_index != destination_index));
208     DRACO_DCHECK_LT(destination_index, n_items_);
209     DRACO_DCHECK_LT(source_index, source.n_items_);
210 
211     // DRACO_DCHECK_EQ(source.n_items_, n_items_); // not technically necessary
212     DRACO_DCHECK_EQ(source.dimensionality_, dimensionality_);
213 
214     const internal_t *ref = source[source_index];
215     internal_t *const dest = this->operator[](destination_index);
216     std::memcpy(dest, ref, item_size_bytes_);
217   }
218 
219   // Copy data directly off of an attribute buffer interleaved into internal
220   // memory.
CopyAttribute(const internal_t attribute_dimensionality,const internal_t offset_dimensionality,const internal_t index,const void * const attribute_item_data)221   void CopyAttribute(
222       // The dimensionality of the attribute being integrated
223       const internal_t attribute_dimensionality,
224       // The offset in dimensions to insert this attribute.
225       const internal_t offset_dimensionality, const internal_t index,
226       // The direct pointer to the data
227       const void *const attribute_item_data) {
228     // chunk copy
229     const size_t copy_size = sizeof(internal_t) * attribute_dimensionality;
230 
231     // a multiply and add can be optimized away with an iterator
232     std::memcpy(data0_ + index * dimensionality_ + offset_dimensionality,
233                 attribute_item_data, copy_size);
234   }
235   // Copy data off of a contiguous buffer interleaved into internal memory
CopyAttribute(const internal_t attribute_dimensionality,const internal_t offset_dimensionality,const internal_t * const attribute_mem)236   void CopyAttribute(
237       // The dimensionality of the attribute being integrated
238       const internal_t attribute_dimensionality,
239       // The offset in dimensions to insert this attribute.
240       const internal_t offset_dimensionality,
241       const internal_t *const attribute_mem) {
242     DRACO_DCHECK_LT(offset_dimensionality,
243                     dimensionality_ - attribute_dimensionality);
244     // degenerate case block copy the whole buffer.
245     if (dimensionality_ == attribute_dimensionality) {
246       DRACO_DCHECK_EQ(offset_dimensionality, 0);
247       const size_t copy_size =
248           sizeof(internal_t) * attribute_dimensionality * n_items_;
249       std::memcpy(data0_, attribute_mem, copy_size);
250     } else {  // chunk copy
251       const size_t copy_size = sizeof(internal_t) * attribute_dimensionality;
252       internal_t *internal_data;
253       const internal_t *attribute_data;
254       internal_t item;
255       for (internal_data = data0_ + offset_dimensionality,
256           attribute_data = attribute_mem, item = 0;
257            item < n_items_; internal_data += dimensionality_,
258           attribute_data += attribute_dimensionality, item += 1) {
259         std::memcpy(internal_data, attribute_data, copy_size);
260       }
261     }
262   }
263 
264  private:
265   // internal parameters.
266   const uint32_t n_items_;
267   const uint32_t dimensionality_;  // The dimension of the points in the buffer
268   const uint32_t item_size_bytes_;
269   std::vector<internal_t> data_;  // contiguously stored data.  Never resized.
270   internal_t *const data0_;       // raw pointer to base data.
271 };
272 
273 }  // namespace draco
274 
275 #endif  // DRACO_COMPRESSION_ATTRIBUTES_POINT_D_VECTOR_H_
276