-
Notifications
You must be signed in to change notification settings - Fork 0
/
parray.h
628 lines (526 loc) · 40 KB
/
parray.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
/*/////////////////////////////////////////////////////////////////////////////
ADV library
Author:
Michael Kilburn
/////////////////////////////////////////////////////////////////////////////*/
#ifndef PARRAY_H_2016_08_30_04_03_32_135_H_
#define PARRAY_H_2016_08_30_04_03_32_135_H_
#include <cstddef>
#include <type_traits>
#include <cstring>
#include <string>
#include <vector>
#include <ostream>
//------------------------------------------------------------------------------
// parray<T, Traits>
//
// Zero-weight class designed to represent a raw pointer to an array (i.e. it doesn't assume ownership),
// it is essentially a (len, T*) pair with associated comparison semantics (implemented in Traits). Default
// comparisons are defined in such way that length takes precedence over elements -- i.e. shorter array is
// always 'less' than longer one. This allows for very efficient comparisons -- in general most comparisons
// don't even need to dereference T*.
// For char types there are tools to deal with trailing nul character (see ntba() & ntbs()). And if T can
// be compared with E then parray<T, Traits> can be compared with parray<E, Traits>.
//
// Examples:
//
// typedef adv::parray<char const> rcstring;
// rcstring r1{ ntba("123") }; // len = 3
//
// char const* p = "123";
// rcstring r2{ ntbs(p) }; // len = strlen(p) == 3
//
// assert( r1 == r2 );
// assert( r1 == ntba("123") ); // will use length of "123" known at compile time
// assert( r1 == ntbs(p) ); // will scan 'p' at most once
// r2 == p; // won't compile, can't get array length from raw pointer
// r2 == "123"; // won't compile, for char array it is impossible to tell if it is nul-terminated or not
//
// int n[] = {1,2,3};
// parray<int> r3 = n; // len = 3
// assert( r3 == n ); // int is not char type -- no problems with nul-termination
// assert( r3 < r1 ); // r3.len == r1.len && 1 < '1'
//
// rcstring r4{}; // force zero initialization
// assert( r4 < r1 );
//
// std::string s("123");
// assert( r1 <= s );
// assert( s == r2 );
//
// std::vector v{1,2,3};
// assert( r3 == v );
//
// Notes:
// - parray<T, Traits> and parray<E, Traits> are assumed comparable (they share same traits class); if T and E aren't comparable
// compilation will fail
// - default trait checks array length first (i.e. ntba("abc") < ntba("aaaaaaa"))
// - passing nullptr to ntbs() is an undefined behavior (NULL was never a valid NTBS)
// - certain conversions are explicit:
// - string/vector -> parray -- we turn object that owns underlying resource into one that doesn't, this prevents problems like:
// string foo() { ... }
// rcstring r = foo();
// - ntbs -> parray -- to find ntbs length we have to scan entire string, it is expensive
// - parray<T, Trait1> -> parray<E, Trat2> -- changing trait means changing comparison behavior
// - 'not_eq' is an 'alternative token' for ^ in C++, using yodaism 'eq_not' instead
// - default trait assumes that comparisons are always commutative, i.e. l == r --> r == l, l < r --> r > l, etc
// - if len == 0 'p' is never used. I guess you could (probably!) squeeze few extra CPU cycles by not populating 'p' if len == 0, but
// compiler sometimes complains about potential use of uninitialized variable... so I left it as it is
// - some ntbs comparisons rely on assumption that for any pair of char types (L and R) following is correct:
// L l = ...;
// R r = ...;
// remove_cv<L> const l_nul{};
// remove_cv<R> const r_nul{};
// l == r && l == l_nul -> r == r_nul; // i.e. if l == r and l is NUL then r is NUL too
// - should probably sprinkle constexpr everywhere...
//
//------------------------------------------------------------------------------
namespace adv { namespace parray_pvt_ {
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Helpers
//
using std::size_t;
using std::basic_string;
using std::char_traits;
using std::vector;
using std::allocator;
using std::memcmp;
using std::basic_ostream;
template<class T> using remove_cv = std::remove_cv_t<T>;
template<bool B, class T = void> using enable_if = std::enable_if_t<B, T>;
template<class T, class U> constexpr bool is_same = std::is_same<T, U>::value;
template<class T> constexpr bool is_volatile = std::is_volatile<T>::value;
template<class T> constexpr bool is_scalar = std::is_scalar<T>::value;
template<class F, class T> constexpr bool is_convertible = std::is_convertible<F, T>::value;
template<class T> constexpr bool is_char = is_same<remove_cv<T>, char> || is_same<remove_cv<T>, wchar_t> || is_same<remove_cv<T>, char16_t> || is_same<remove_cv<T>, char32_t>;
template<class T> constexpr bool is_byte = is_same<remove_cv<T>, unsigned char>;
// relaxed version of 'is_same<remove_cv<E>, remove_cv<T>> && is_convertible<E*, T*>'
template<class E, class T> constexpr bool is_almost_same = (sizeof(E) == sizeof(T)) && is_convertible<E*, T*>;
//------------------------------------------------------------------------------
struct parray_traits
{
//
// arrays
//
protected:
static bool same_ptr(void volatile const* l, void volatile const* r) { return l == r; }
// element comparisons
// using char_traits to compare chars of same base type
template<class L, class R, enable_if< is_same<remove_cv<L>, remove_cv<R>> && is_char<L> >...> constexpr static bool elem_eq(L l, R r) { return char_traits<remove_cv<L>>::eq(l, r); }
template<class L, class R, enable_if< is_same<remove_cv<L>, remove_cv<R>> && is_char<L> >...> constexpr static bool elem_lt(L l, R r) { return char_traits<remove_cv<L>>::lt(l, r); }
// using operators for everything else
template<class L, class R, enable_if<!(is_same<remove_cv<L>, remove_cv<R>> && is_char<L>)>...> constexpr static bool elem_eq(L& l, R& r) { return l == r; }
template<class L, class R, enable_if<!(is_same<remove_cv<L>, remove_cv<R>> && is_char<L>)>...> constexpr static bool elem_lt(L& l, R& r) { return l < r; }
// generic comparisons -- used instead of specialized version (when related conditions aren't met)
template<class L, class R>
static bool generic_eq(size_t len, L* l, R* r)
{
for(; len > 0; --len, ++l, ++r) { if (!elem_eq(*l, *r)) return false; }
return true;
}
template<class L, class R>
static bool generic_lt(size_t len, L* l, R* r)
{
for(; len > 0; --len, ++l, ++r) { if (!elem_eq(*l, *r)) return elem_lt(*l, *r); }
return false;
}
// non-volatile chars -> char_traits, non-volatile unsigned chars -> memcmp, rest -> generic algorithm
template<class L, class R> constexpr static bool is_case1 = !is_volatile<L> && !is_volatile<R> && is_char<L> && is_char<R> && is_same<remove_cv<L>, remove_cv<R>>;
template<class L, class R> constexpr static bool is_case2 = !is_volatile<L> && !is_volatile<R> && is_byte<L> && is_byte<R>;
template<class L, class R> constexpr static bool is_case3 = !(is_case1<L, R> || is_case2<L, R>);
template<class L, class R, enable_if<is_case1<L, R>>...> static bool ar_eq(size_t len, L* l, R* r) { return char_traits<remove_cv<L>>::compare(l, r, len) == 0; }
template<class L, class R, enable_if<is_case1<L, R>>...> static bool ar_lt(size_t len, L* l, R* r) { return char_traits<remove_cv<L>>::compare(l, r, len) < 0; }
template<class L, class R, enable_if<is_case2<L, R>>...> static bool ar_eq(size_t len, L* l, R* r) { return memcmp(l, r, len) == 0; }
template<class L, class R, enable_if<is_case2<L, R>>...> static bool ar_lt(size_t len, L* l, R* r) { return memcmp(l, r, len) < 0; }
template<class L, class R, enable_if<is_case3<L, R>>...> static bool ar_eq(size_t len, L* l, R* r) { return generic_eq(len, l, r); }
template<class L, class R, enable_if<is_case3<L, R>>...> static bool ar_lt(size_t len, L* l, R* r) { return generic_lt(len, l, r); }
// user can't overload operators for these types -- we can use a bit of optimization
template<class L, class R, enable_if< is_scalar<L> && is_scalar<R>>...> static bool ar_eq_(size_t len, L* l, R* r) { return same_ptr (l, r) || ar_eq(len, l, r); }
template<class L, class R, enable_if< is_scalar<L> && is_scalar<R>>...> static bool ar_lt_(size_t len, L* l, R* r) { return !same_ptr(l, r) && ar_lt(len, l, r); }
// user can overload operators for these types -- stay safe in case he did smth very unusual (like making two objects at the same address not equal)
template<class L, class R, enable_if<!is_scalar<L> || !is_scalar<R>>...> static bool ar_eq_(size_t len, L* l, R* r) { return ar_eq(len, l, r); }
template<class L, class R, enable_if<!is_scalar<L> || !is_scalar<R>>...> static bool ar_lt_(size_t len, L* l, R* r) { return ar_lt(len, l, r); }
public:
// comparisons
template<class L, class R> static bool eq (size_t l_len, L* l, size_t r_len, R* r) { return l_len == r_len && ar_eq_(l_len, l, r); }
template<class L, class R> static bool eq_not(size_t l_len, L* l, size_t r_len, R* r) { return !eq(l_len, l, r_len, r); } // l != r -> !(l == r)
template<class L, class R> static bool lt (size_t l_len, L* l, size_t r_len, R* r) { return l_len < r_len || (l_len == r_len && ar_lt_(l_len, l, r)); }
template<class L, class R> static bool gt (size_t l_len, L* l, size_t r_len, R* r) { return lt (r_len, r, l_len, l); } // l > r -> r < l
template<class L, class R> static bool lt_eq (size_t l_len, L* l, size_t r_len, R* r) { return !lt(r_len, r, l_len, l); } // l <= r -> r >= l -> !(r < l)
template<class L, class R> static bool gt_eq (size_t l_len, L* l, size_t r_len, R* r) { return !lt(l_len, l, r_len, r); } // l >= r -> !(l < r)
//
// ntbs
//
protected:
// using generic algorithm for all cases (in our comparisons length trumps elements -- there are no library functions with same behavior)
template<class L, class R>
static bool ntbs_ar_eq(L* l, R* r)
{
static_assert(remove_cv<L>{} == remove_cv<R>{}, ""); // this should hold for any T that is is_char<T>
for(remove_cv<L> const l_nul{}; ; ++l, ++r)
{
if (elem_eq(*l, *r))
if (!elem_eq(*l, l_nul))
;
else
return true; // l_nul == r_nul && *l == *r && *l == l_nul -> *r == r_nul -> arrays are equal
else
return false;
}
}
template<class L, class R>
static bool ntbs_ar_eq(size_t l_len, L* l, R* r)
{
L* l_end = l + l_len;
remove_cv<R> const r_nul{};
for(;; ++l, ++r)
{
bool l_done = (l == l_end);
bool r_done = elem_eq(*r, r_nul);
if (l_done || r_done) return l_done && r_done; // true if lengths are same
if (!elem_eq(*l, *r)) return false; // we found non-NUL elements that differ -- arrays not equal
}
}
template<class L, class R>
static bool ntbs_ar_lt(L* l, R* r)
{
static_assert(remove_cv<L>{} == remove_cv<R>{}, ""); // this should hold for any T that is is_char<T>
for(remove_cv<L> const l_nul{}; ; ++l, ++r)
{
if (elem_eq(*l, *r))
{
if (!elem_eq(*l, l_nul))
;
else
return false; // l_nul == r_nul && *l == *r && *l == l_nul -> *r == r_nul -> l == r -> !(l < r)
}
else // *l != *r
{
if (elem_eq(*l, l_nul)) return true; // *l == l_nul && *l != *r -> *r != nul -> l < r
remove_cv<R> const r_nul{};
if (elem_eq(*r, r_nul)) return false; // *r == r_nul && *l != *r -> *l != nul -> !(l < r)
for(bool lexicographical_lt = elem_lt(*l++, *r++); ; ++l, ++r)
{
bool l_done = elem_eq(*l, l_nul);
bool r_done = elem_eq(*r, r_nul);
if (l_done || r_done)
return (l_done && r_done) ? lexicographical_lt : l_done;
}
}
}
}
template<class L, class R>
static bool ntbs_ar_lt(size_t l_len, L* l, R* r)
{
L* l_end = l + l_len;
remove_cv<R> const r_nul{};
for(;; ++l, ++r)
{
bool l_done = (l == l_end);
bool r_done = elem_eq(*r, r_nul);
if (l_done || r_done) return l_done && !r_done; // true if only 'l' end is reached
if (!elem_eq(*l, *r)) // arrays not equal, lets see if 'l < r'
{
for(bool lexicographical_lt = elem_lt(*l++, *r++); ; ++l, ++r)
{
bool l_done = (l == l_end);
bool r_done = elem_eq(*r, r_nul);
if (l_done || r_done)
return (l_done && r_done) ? lexicographical_lt : l_done;
}
}
}
}
template<class L, class R>
static bool ntbs_ar_lt(L* l, size_t r_len, R* r)
{
remove_cv<L> const l_nul{};
R* r_end = r + r_len;
for(;; ++l, ++r)
{
bool l_done = elem_eq(*l, l_nul);
bool r_done = (r == r_end);
if (l_done || r_done) return l_done && !r_done; // true if only 'l' end is reached
if (!elem_eq(*l, *r)) // arrays not equal, lets see if 'l < r'
{
for(bool lexicographical_lt = elem_lt(*l++, *r++); ; ++l, ++r)
{
bool l_done = elem_eq(*l, l_nul);
bool r_done = (r == r_end);
if (l_done || r_done)
return (l_done && r_done) ? lexicographical_lt : l_done;
}
}
}
}
// user can't overload operators for these types -- we can use a bit of optimization
template<class L, class R, enable_if< is_scalar<L> && is_scalar<R>>...> static bool ntbs_ar_eq_(L* l, R* r) { return same_ptr (l, r) || ntbs_ar_eq(l, r); }
template<class L, class R, enable_if< is_scalar<L> && is_scalar<R>>...> static bool ntbs_ar_lt_(L* l, R* r) { return !same_ptr(l, r) && ntbs_ar_lt(l, r); }
// user can overload operators for these types -- stay safe in case he did smth very unusual (like making two objects at the same address not equal)
template<class L, class R, enable_if<!is_scalar<L> || !is_scalar<R>>...> static bool ntbs_ar_eq_(L* l, R* r) { return ntbs_ar_eq(l, r); }
template<class L, class R, enable_if<!is_scalar<L> || !is_scalar<R>>...> static bool ntbs_ar_lt_(L* l, R* r) { return ntbs_ar_lt(l, r); }
public:
// length
template<class T, enable_if<is_char<T> && !is_volatile<T>>...> // char_traits doesn't handle volatile
static size_t ntbs_len(T* p) { return char_traits<remove_cv<T>>::length(p); }
template<class T, enable_if<!is_char<T> || is_volatile<T>>...>
static size_t ntbs_len(T* p)
{
size_t count = 0;
for(remove_cv<T> const nul{}; !elem_eq(*p, nul); ++p) ++count;
return count;
}
// comparisons
template<class L, class R> static bool ntbs_eq (L* l, R* r) { return ntbs_ar_eq_(l, r); }
template<class L, class R> static bool ntbs_not_eq(L* l, R* r) { return !ntbs_eq (l, r); } // l != r -> !(l == r)
template<class L, class R> static bool ntbs_lt (L* l, R* r) { return ntbs_ar_lt_(l, r); }
template<class L, class R> static bool ntbs_gt (L* l, R* r) { return ntbs_lt (r, l); } // l > r -> r < l
template<class L, class R> static bool ntbs_lt_eq (L* l, R* r) { return !ntbs_lt (r, l); } // l <= r -> r >= l -> !(r < l)
template<class L, class R> static bool ntbs_gt_eq (L* l, R* r) { return !ntbs_lt (l, r); } // l >= r -> !(l < r)
template<class L, class R> static bool ntbs_eq (size_t l_len, L* l, R* r) { return ntbs_ar_eq(l_len, l, r); }
template<class L, class R> static bool ntbs_not_eq(size_t l_len, L* l, R* r) { return !ntbs_eq (l_len, l, r); } // l != r -> !(l == r)
template<class L, class R> static bool ntbs_lt (size_t l_len, L* l, R* r) { return ntbs_ar_lt(l_len, l, r); }
template<class L, class R> static bool ntbs_gt (size_t l_len, L* l, R* r) { return ntbs_lt (r, l_len, l); } // l > r -> r < l
template<class L, class R> static bool ntbs_lt_eq (size_t l_len, L* l, R* r) { return !ntbs_lt (r, l_len, l); } // l <= r -> r >= l -> !(r < l)
template<class L, class R> static bool ntbs_gt_eq (size_t l_len, L* l, R* r) { return !ntbs_lt (l_len, l, r); } // l >= r -> !(l < r)
template<class L, class R> static bool ntbs_eq (L* l, size_t r_len, R* r) { return ntbs_eq (r_len, r, l); } // l == r -> r == l
template<class L, class R> static bool ntbs_not_eq(L* l, size_t r_len, R* r) { return !ntbs_eq (l, r_len, r); } // l != r -> !(l == r)
template<class L, class R> static bool ntbs_lt (L* l, size_t r_len, R* r) { return ntbs_ar_lt(l, r_len, r); }
template<class L, class R> static bool ntbs_gt (L* l, size_t r_len, R* r) { return ntbs_lt (r_len, r, l); } // l > r -> r < l
template<class L, class R> static bool ntbs_lt_eq (L* l, size_t r_len, R* r) { return !ntbs_lt (r_len, r, l); } // l <= r -> r >= l -> !(r < l)
template<class L, class R> static bool ntbs_gt_eq (L* l, size_t r_len, R* r) { return !ntbs_lt (l, r_len, r); } // l >= r -> !(l < r)
};
//------------------------------------------------------------------------------
template<class T, class Traits = parray_traits>
struct parray
{
typedef T ElemT;
typedef remove_cv<T> Tc;
size_t len;
T* p; // not used if len == 0
parray() = default; // use default uinitialization by default (use parray<...> v{} to force zero initialization)
// implicit dtor, cctor & op= are ok
// implicit mctor & opm= are ok
template<class E, enable_if<is_almost_same<E, T>>...> // (E*, size_t) -> parray<T> (E* -> T*)
parray(size_t len, E* p) : len(len), p(p) {}
template<class E, enable_if<is_almost_same<E, T>>...> // parray<E> -> parray<T> (E* -> T*), implicit
parray(parray<E, Traits> o) : len(o.len), p(o.p) {}
template<class E, class Traits2, enable_if<is_almost_same<E, T>>...> // parray<E, Traits2> -> parray<T, Traits> (E* -> T*), explicit
explicit parray(parray<E, Traits2> o) : len(o.len), p(o.p) {}
template<class E, size_t len, enable_if<!is_char<E> && is_almost_same<E, T>>...> // E[] -> parray<T> (E* -> T*), E != char
parray(E (&r)[len]) : len(len), p(r) {}
template<class E, class Tr, class A, enable_if<is_almost_same<E const, T>>...> // string<E> -> parray<T> (E const* -> T*)
explicit parray(basic_string<E, Tr, A> const& s) : len(s.size()), p(len ? s.data() : nullptr) {}
template<class E, class A, enable_if<is_almost_same<E, T>>...> // vector<E> -> parray<T> (E* -> T*)
explicit parray(vector<E, A>& v) : len(v.size()), p(len ? &v[0] : nullptr) {}
template<class E, class A, enable_if<is_almost_same<E const, T>>...> // vector<E> const -> parray<T> (E const* -> T*)
explicit parray(vector<E, A> const& v) : len(v.size()), p(len ? &v[0] : nullptr) {}
// subparts
parray left(size_t cnt) const { return {cnt, p}; }
parray right(size_t cnt) const { return {cnt, p + len - cnt}; }
parray mid(size_t pos, size_t cnt) const { return {cnt, p + pos}; }
// cheap access
size_t size() const { return len; }
bool empty() const { return len == 0; }
T& operator[](size_t i) const { return p[i]; }
// expensive conversions
template<class Tr = char_traits<Tc>, class A = allocator<Tc>>
basic_string<Tc, Tr, A> str() const { return {p, len}; }
template<class A = allocator<Tc>>
vector<Tc, A> vec() const { return {p, p + len}; }
// comparison operators (forwarding everything to trait in case if user wants to force unusual behaviour)
// parray<T, Traits> vs parray<E, Traits>
template<class E> friend bool operator==(parray l, parray<E, Traits> r) { return Traits::eq (l.len, l.p, r.len, r.p); }
template<class E> friend bool operator!=(parray l, parray<E, Traits> r) { return Traits::eq_not(l.len, l.p, r.len, r.p); }
template<class E> friend bool operator< (parray l, parray<E, Traits> r) { return Traits::lt (l.len, l.p, r.len, r.p); }
template<class E> friend bool operator> (parray l, parray<E, Traits> r) { return Traits::gt (l.len, l.p, r.len, r.p); }
template<class E> friend bool operator<=(parray l, parray<E, Traits> r) { return Traits::lt_eq (l.len, l.p, r.len, r.p); }
template<class E> friend bool operator>=(parray l, parray<E, Traits> r) { return Traits::gt_eq (l.len, l.p, r.len, r.p); }
// parray<T, Traits> vs basic_string<E, Tr, A>
template<class E, class Tr, class A> friend bool operator==(parray l, basic_string<E, Tr, A> const& r) { return l == parray<E const, Traits>(r); }
template<class E, class Tr, class A> friend bool operator!=(parray l, basic_string<E, Tr, A> const& r) { return l != parray<E const, Traits>(r); }
template<class E, class Tr, class A> friend bool operator< (parray l, basic_string<E, Tr, A> const& r) { return l < parray<E const, Traits>(r); }
template<class E, class Tr, class A> friend bool operator> (parray l, basic_string<E, Tr, A> const& r) { return l > parray<E const, Traits>(r); }
template<class E, class Tr, class A> friend bool operator<=(parray l, basic_string<E, Tr, A> const& r) { return l <= parray<E const, Traits>(r); }
template<class E, class Tr, class A> friend bool operator>=(parray l, basic_string<E, Tr, A> const& r) { return l >= parray<E const, Traits>(r); }
template<class E, class Tr, class A> friend bool operator==(basic_string<E, Tr, A> const& l, parray r) { return parray<E const, Traits>(l) == r; }
template<class E, class Tr, class A> friend bool operator!=(basic_string<E, Tr, A> const& l, parray r) { return parray<E const, Traits>(l) != r; }
template<class E, class Tr, class A> friend bool operator< (basic_string<E, Tr, A> const& l, parray r) { return parray<E const, Traits>(l) < r; }
template<class E, class Tr, class A> friend bool operator> (basic_string<E, Tr, A> const& l, parray r) { return parray<E const, Traits>(l) > r; }
template<class E, class Tr, class A> friend bool operator<=(basic_string<E, Tr, A> const& l, parray r) { return parray<E const, Traits>(l) <= r; }
template<class E, class Tr, class A> friend bool operator>=(basic_string<E, Tr, A> const& l, parray r) { return parray<E const, Traits>(l) >= r; }
// parray<T, Traits> vs vector<E, A>
template<class E, class A> friend bool operator==(parray l, vector<E, A> const& r) { return l == parray<E const, Traits>(r); }
template<class E, class A> friend bool operator!=(parray l, vector<E, A> const& r) { return l != parray<E const, Traits>(r); }
template<class E, class A> friend bool operator< (parray l, vector<E, A> const& r) { return l < parray<E const, Traits>(r); }
template<class E, class A> friend bool operator> (parray l, vector<E, A> const& r) { return l > parray<E const, Traits>(r); }
template<class E, class A> friend bool operator<=(parray l, vector<E, A> const& r) { return l <= parray<E const, Traits>(r); }
template<class E, class A> friend bool operator>=(parray l, vector<E, A> const& r) { return l >= parray<E const, Traits>(r); }
template<class E, class A> friend bool operator==(vector<E, A> const& l, parray r) { return parray<E const, Traits>(l) == r; }
template<class E, class A> friend bool operator!=(vector<E, A> const& l, parray r) { return parray<E const, Traits>(l) != r; }
template<class E, class A> friend bool operator< (vector<E, A> const& l, parray r) { return parray<E const, Traits>(l) < r; }
template<class E, class A> friend bool operator> (vector<E, A> const& l, parray r) { return parray<E const, Traits>(l) > r; }
template<class E, class A> friend bool operator<=(vector<E, A> const& l, parray r) { return parray<E const, Traits>(l) <= r; }
template<class E, class A> friend bool operator>=(vector<E, A> const& l, parray r) { return parray<E const, Traits>(l) >= r; }
template<class E, class A> friend bool operator==(parray l, vector<E, A>& r) { return l == parray<E, Traits>(r); }
template<class E, class A> friend bool operator!=(parray l, vector<E, A>& r) { return l != parray<E, Traits>(r); }
template<class E, class A> friend bool operator< (parray l, vector<E, A>& r) { return l < parray<E, Traits>(r); }
template<class E, class A> friend bool operator> (parray l, vector<E, A>& r) { return l > parray<E, Traits>(r); }
template<class E, class A> friend bool operator<=(parray l, vector<E, A>& r) { return l <= parray<E, Traits>(r); }
template<class E, class A> friend bool operator>=(parray l, vector<E, A>& r) { return l >= parray<E, Traits>(r); }
template<class E, class A> friend bool operator==(vector<E, A>& l, parray r) { return parray<E, Traits>(l) == r; }
template<class E, class A> friend bool operator!=(vector<E, A>& l, parray r) { return parray<E, Traits>(l) != r; }
template<class E, class A> friend bool operator< (vector<E, A>& l, parray r) { return parray<E, Traits>(l) < r; }
template<class E, class A> friend bool operator> (vector<E, A>& l, parray r) { return parray<E, Traits>(l) > r; }
template<class E, class A> friend bool operator<=(vector<E, A>& l, parray r) { return parray<E, Traits>(l) <= r; }
template<class E, class A> friend bool operator>=(vector<E, A>& l, parray r) { return parray<E, Traits>(l) >= r; }
// parray<T, Traits> vs E[len]
template<class E, size_t len, enable_if<!is_char<E>>...> friend bool operator==(parray l, E (&r)[len]) { return l == parray<E, Traits>(r); }
template<class E, size_t len, enable_if<!is_char<E>>...> friend bool operator!=(parray l, E (&r)[len]) { return l != parray<E, Traits>(r); }
template<class E, size_t len, enable_if<!is_char<E>>...> friend bool operator< (parray l, E (&r)[len]) { return l < parray<E, Traits>(r); }
template<class E, size_t len, enable_if<!is_char<E>>...> friend bool operator> (parray l, E (&r)[len]) { return l > parray<E, Traits>(r); }
template<class E, size_t len, enable_if<!is_char<E>>...> friend bool operator<=(parray l, E (&r)[len]) { return l <= parray<E, Traits>(r); }
template<class E, size_t len, enable_if<!is_char<E>>...> friend bool operator>=(parray l, E (&r)[len]) { return l >= parray<E, Traits>(r); }
template<class E, size_t len, enable_if<!is_char<E>>...> friend bool operator==(E (&l)[len], parray r) { return parray<E, Traits>(l) == r; }
template<class E, size_t len, enable_if<!is_char<E>>...> friend bool operator!=(E (&l)[len], parray r) { return parray<E, Traits>(l) != r; }
template<class E, size_t len, enable_if<!is_char<E>>...> friend bool operator< (E (&l)[len], parray r) { return parray<E, Traits>(l) < r; }
template<class E, size_t len, enable_if<!is_char<E>>...> friend bool operator> (E (&l)[len], parray r) { return parray<E, Traits>(l) > r; }
template<class E, size_t len, enable_if<!is_char<E>>...> friend bool operator<=(E (&l)[len], parray r) { return parray<E, Traits>(l) <= r; }
template<class E, size_t len, enable_if<!is_char<E>>...> friend bool operator>=(E (&l)[len], parray r) { return parray<E, Traits>(l) >= r; }
// ostream <<
template<class E, class Tr, enable_if<is_almost_same<T, E const>>...> friend basic_ostream<E, Tr>& operator<<(basic_ostream<E, Tr>& os, parray v) { return os.write(v.p, v.len); }
};
//------------------------------------------------------------------------------
// Null-terminated array -- array with last element that is to be ignored (literal string), defined only for non-empty char arrays
//
template<class Traits = parray_traits, class T, size_t len, enable_if<(len > 0) && is_char<T>>...>
inline parray<T, Traits> ntba(T (&r)[len]) { return {len - 1, r}; }
//------------------------------------------------------------------------------
// Null-terminated string -- pointer to a NUL-terminated string (C-style)
//
// Notes:
// - it is inconvenient to pass array into this function (as intended) -- NUL-terminated arrays are supposed to be handled by 'ntba()'
// - in case if your array holds string of unknown length (often the case when using things like db-lib) you can:
// - (expensive) force length recalc via ntbs(&buffer[0]) or
// - (preferred) use already precalculated length (e.g. len = dbdatlen(...)): rcstring(len, buffer)
// - be careful with return value -- don't recalc length repeatedly, e.g.:
// void foo(rcstring v) { ... }
// ...
// auto s = ntbs(...);
// for(...)
// foo( rcstring(s) ); // bad, length gets calculated here repeatedly
// - same for comparisons: if same ntbs is going to participate in multiple comparisons -- it is better to convert it into parray
//
template<class T, class Traits>
struct ntbs_t
{
T* p;
template<class E, class Traits2, enable_if<is_almost_same<T, E>>...>
explicit operator parray<E, Traits2>() const { return {Traits::ntbs_len(p), p}; }
// comparisons
// ntbs_t<T, Traits> vs ntbs<E, Traits>
template<class E> friend bool operator==(ntbs_t l, ntbs_t<E, Traits> r) { return Traits::ntbs_eq (l.p, r.p); }
template<class E> friend bool operator!=(ntbs_t l, ntbs_t<E, Traits> r) { return Traits::ntbs_not_eq(l.p, r.p); }
template<class E> friend bool operator< (ntbs_t l, ntbs_t<E, Traits> r) { return Traits::ntbs_lt (l.p, r.p); }
template<class E> friend bool operator> (ntbs_t l, ntbs_t<E, Traits> r) { return Traits::ntbs_gt (l.p, r.p); }
template<class E> friend bool operator<=(ntbs_t l, ntbs_t<E, Traits> r) { return Traits::ntbs_lt_eq (l.p, r.p); }
template<class E> friend bool operator>=(ntbs_t l, ntbs_t<E, Traits> r) { return Traits::ntbs_gt_eq (l.p, r.p); }
// ntbs_t<T, Traits> vs parray<E, Traits>
template<class E> friend bool operator==(ntbs_t l, parray<E, Traits> r) { return Traits::ntbs_eq (l.p, r.len, r.p); }
template<class E> friend bool operator!=(ntbs_t l, parray<E, Traits> r) { return Traits::ntbs_not_eq(l.p, r.len, r.p); }
template<class E> friend bool operator< (ntbs_t l, parray<E, Traits> r) { return Traits::ntbs_lt (l.p, r.len, r.p); }
template<class E> friend bool operator> (ntbs_t l, parray<E, Traits> r) { return Traits::ntbs_gt (l.p, r.len, r.p); }
template<class E> friend bool operator<=(ntbs_t l, parray<E, Traits> r) { return Traits::ntbs_lt_eq (l.p, r.len, r.p); }
template<class E> friend bool operator>=(ntbs_t l, parray<E, Traits> r) { return Traits::ntbs_gt_eq (l.p, r.len, r.p); }
template<class E> friend bool operator==(parray<E, Traits> l, ntbs_t r) { return Traits::ntbs_eq (l.len, l.p, r.p); }
template<class E> friend bool operator!=(parray<E, Traits> l, ntbs_t r) { return Traits::ntbs_not_eq(l.len, l.p, r.p); }
template<class E> friend bool operator< (parray<E, Traits> l, ntbs_t r) { return Traits::ntbs_lt (l.len, l.p, r.p); }
template<class E> friend bool operator> (parray<E, Traits> l, ntbs_t r) { return Traits::ntbs_gt (l.len, l.p, r.p); }
template<class E> friend bool operator<=(parray<E, Traits> l, ntbs_t r) { return Traits::ntbs_lt_eq (l.len, l.p, r.p); }
template<class E> friend bool operator>=(parray<E, Traits> l, ntbs_t r) { return Traits::ntbs_gt_eq (l.len, l.p, r.p); }
// ntbs_t<T, Traits> vs basic_string<E, Tr, A>
template<class E, class Tr, class A> friend bool operator==(ntbs_t l, basic_string<E, Tr, A> const& r) { return l == parray<E const, Traits>(r); }
template<class E, class Tr, class A> friend bool operator!=(ntbs_t l, basic_string<E, Tr, A> const& r) { return l != parray<E const, Traits>(r); }
template<class E, class Tr, class A> friend bool operator< (ntbs_t l, basic_string<E, Tr, A> const& r) { return l < parray<E const, Traits>(r); }
template<class E, class Tr, class A> friend bool operator> (ntbs_t l, basic_string<E, Tr, A> const& r) { return l > parray<E const, Traits>(r); }
template<class E, class Tr, class A> friend bool operator<=(ntbs_t l, basic_string<E, Tr, A> const& r) { return l <= parray<E const, Traits>(r); }
template<class E, class Tr, class A> friend bool operator>=(ntbs_t l, basic_string<E, Tr, A> const& r) { return l >= parray<E const, Traits>(r); }
template<class E, class Tr, class A> friend bool operator==(basic_string<E, Tr, A> const& l, ntbs_t r) { return parray<E const, Traits>(l) == r; }
template<class E, class Tr, class A> friend bool operator!=(basic_string<E, Tr, A> const& l, ntbs_t r) { return parray<E const, Traits>(l) != r; }
template<class E, class Tr, class A> friend bool operator< (basic_string<E, Tr, A> const& l, ntbs_t r) { return parray<E const, Traits>(l) < r; }
template<class E, class Tr, class A> friend bool operator> (basic_string<E, Tr, A> const& l, ntbs_t r) { return parray<E const, Traits>(l) > r; }
template<class E, class Tr, class A> friend bool operator<=(basic_string<E, Tr, A> const& l, ntbs_t r) { return parray<E const, Traits>(l) <= r; }
template<class E, class Tr, class A> friend bool operator>=(basic_string<E, Tr, A> const& l, ntbs_t r) { return parray<E const, Traits>(l) >= r; }
// ntbs_t<T, Traits> vs vector<E, A>
template<class E, class A> friend bool operator==(ntbs_t l, vector<E, A> const& r) { return l == parray<E const, Traits>(r); }
template<class E, class A> friend bool operator!=(ntbs_t l, vector<E, A> const& r) { return l != parray<E const, Traits>(r); }
template<class E, class A> friend bool operator< (ntbs_t l, vector<E, A> const& r) { return l < parray<E const, Traits>(r); }
template<class E, class A> friend bool operator> (ntbs_t l, vector<E, A> const& r) { return l > parray<E const, Traits>(r); }
template<class E, class A> friend bool operator<=(ntbs_t l, vector<E, A> const& r) { return l <= parray<E const, Traits>(r); }
template<class E, class A> friend bool operator>=(ntbs_t l, vector<E, A> const& r) { return l >= parray<E const, Traits>(r); }
template<class E, class A> friend bool operator==(vector<E, A> const& l, ntbs_t r) { return parray<E const, Traits>(l) == r; }
template<class E, class A> friend bool operator!=(vector<E, A> const& l, ntbs_t r) { return parray<E const, Traits>(l) != r; }
template<class E, class A> friend bool operator< (vector<E, A> const& l, ntbs_t r) { return parray<E const, Traits>(l) < r; }
template<class E, class A> friend bool operator> (vector<E, A> const& l, ntbs_t r) { return parray<E const, Traits>(l) > r; }
template<class E, class A> friend bool operator<=(vector<E, A> const& l, ntbs_t r) { return parray<E const, Traits>(l) <= r; }
template<class E, class A> friend bool operator>=(vector<E, A> const& l, ntbs_t r) { return parray<E const, Traits>(l) >= r; }
template<class E, class A> friend bool operator==(ntbs_t l, vector<E, A>& r) { return l == parray<E, Traits>(r); }
template<class E, class A> friend bool operator!=(ntbs_t l, vector<E, A>& r) { return l != parray<E, Traits>(r); }
template<class E, class A> friend bool operator< (ntbs_t l, vector<E, A>& r) { return l < parray<E, Traits>(r); }
template<class E, class A> friend bool operator> (ntbs_t l, vector<E, A>& r) { return l > parray<E, Traits>(r); }
template<class E, class A> friend bool operator<=(ntbs_t l, vector<E, A>& r) { return l <= parray<E, Traits>(r); }
template<class E, class A> friend bool operator>=(ntbs_t l, vector<E, A>& r) { return l >= parray<E, Traits>(r); }
template<class E, class A> friend bool operator==(vector<E, A>& l, ntbs_t r) { return parray<E, Traits>(l) == r; }
template<class E, class A> friend bool operator!=(vector<E, A>& l, ntbs_t r) { return parray<E, Traits>(l) != r; }
template<class E, class A> friend bool operator< (vector<E, A>& l, ntbs_t r) { return parray<E, Traits>(l) < r; }
template<class E, class A> friend bool operator> (vector<E, A>& l, ntbs_t r) { return parray<E, Traits>(l) > r; }
template<class E, class A> friend bool operator<=(vector<E, A>& l, ntbs_t r) { return parray<E, Traits>(l) <= r; }
template<class E, class A> friend bool operator>=(vector<E, A>& l, ntbs_t r) { return parray<E, Traits>(l) >= r; }
// ntbs_t<T, Traits> vs E[len]
template<class E, size_t len, enable_if<!is_char<E>>...> friend bool operator==(ntbs_t l, E (&r)[len]) { return l == parray<E, Traits>(r); }
template<class E, size_t len, enable_if<!is_char<E>>...> friend bool operator!=(ntbs_t l, E (&r)[len]) { return l != parray<E, Traits>(r); }
template<class E, size_t len, enable_if<!is_char<E>>...> friend bool operator< (ntbs_t l, E (&r)[len]) { return l < parray<E, Traits>(r); }
template<class E, size_t len, enable_if<!is_char<E>>...> friend bool operator> (ntbs_t l, E (&r)[len]) { return l > parray<E, Traits>(r); }
template<class E, size_t len, enable_if<!is_char<E>>...> friend bool operator<=(ntbs_t l, E (&r)[len]) { return l <= parray<E, Traits>(r); }
template<class E, size_t len, enable_if<!is_char<E>>...> friend bool operator>=(ntbs_t l, E (&r)[len]) { return l >= parray<E, Traits>(r); }
template<class E, size_t len, enable_if<!is_char<E>>...> friend bool operator==(E (&l)[len], ntbs_t r) { return parray<E, Traits>(l) == r; }
template<class E, size_t len, enable_if<!is_char<E>>...> friend bool operator!=(E (&l)[len], ntbs_t r) { return parray<E, Traits>(l) != r; }
template<class E, size_t len, enable_if<!is_char<E>>...> friend bool operator< (E (&l)[len], ntbs_t r) { return parray<E, Traits>(l) < r; }
template<class E, size_t len, enable_if<!is_char<E>>...> friend bool operator> (E (&l)[len], ntbs_t r) { return parray<E, Traits>(l) > r; }
template<class E, size_t len, enable_if<!is_char<E>>...> friend bool operator<=(E (&l)[len], ntbs_t r) { return parray<E, Traits>(l) <= r; }
template<class E, size_t len, enable_if<!is_char<E>>...> friend bool operator>=(E (&l)[len], ntbs_t r) { return parray<E, Traits>(l) >= r; }
// ostream <<
template<class E, class Tr, enable_if<is_almost_same<T, E const>>...> friend basic_ostream<E, Tr>& operator<<(basic_ostream<E, Tr>& os, ntbs_t v) { return os << v.p; }
};
template<class Traits = parray_traits, class T, enable_if<is_char<T>>...>
inline ntbs_t<T, Traits> ntbs(T* const& p) { return {p}; }
//------------------------------------------------------------------------------
} // namespace parray_pvt_
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
using parray_pvt_::parray;
using parray_pvt_::parray_traits;
using parray_pvt_::ntba;
using parray_pvt_::ntbs;
using rbytes = parray<unsigned char>;
using rcbytes = parray<unsigned char const>;
using rstring = parray<char>;
using rcstring = parray<char const>;
using rwstring = parray<wchar_t>;
using rcwstring = parray<wchar_t const>;
//------------------------------------------------------------------------------
inline namespace literals {
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
inline parray<char const> operator""_rs(char const* p, std::size_t len) { return {len, p}; }
inline parray<wchar_t const> operator""_rs(wchar_t const* p, std::size_t len) { return {len, p}; }
inline parray<char16_t const> operator""_rs(char16_t const* p, std::size_t len) { return {len, p}; }
inline parray<char32_t const> operator""_rs(char32_t const* p, std::size_t len) { return {len, p}; }
//------------------------------------------------------------------------------
} // namespace literals
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
} // namespace adv
//------------------------------------------------------------------------------
#endif //PARRAY_H_2016_08_30_04_03_32_135_H_