libpqxx
The C++ client library for PostgreSQL
encodings.hxx
Go to the documentation of this file.
1 
9 #ifndef PQXX_H_ENCODINGS
10 #define PQXX_H_ENCODINGS
11 
12 #include <iomanip>
13 #include <string>
14 #include <string_view>
15 
16 #include "pqxx/internal/concat.hxx"
18 
19 
20 namespace pqxx::internal
21 {
23 PQXX_PURE char const *name_encoding(int encoding_id);
24 
26 PQXX_LIBEXPORT encoding_group enc_group(int /* libpq encoding ID */);
27 
28 
30 
35 
36 
37 // TODO: Get rid of this one. Use compile-time-specialised version instead.
39 
45 template<char... NEEDLE>
46 inline std::size_t find_char(
47  glyph_scanner_func *scanner, std::string_view haystack,
48  std::size_t here = 0u)
49 {
50  auto const sz{std::size(haystack)};
51  auto const data{std::data(haystack)};
52  while (here < sz)
53  {
54  auto next{scanner(data, sz, here)};
55  PQXX_ASSUME(next > here);
56  // (For some reason gcc had a problem with a right-fold here. But clang
57  // was fine.)
58  if ((... or (data[here] == NEEDLE)))
59  {
60  // Also check against a multibyte character starting with a bytes which
61  // just happens to match one of the ASCII bytes we're looking for. It'd
62  // be cleaner to check that first, but either works. So, let's apply the
63  // most selective filter first and skip this check in almost all cases.
64  if (next == here + 1)
65  return here;
66  }
67 
68  // Nope, no hit. Move on.
69  here = next;
70  }
71  return sz;
72 }
73 
74 
75 // TODO: Get rid of this one. Use compile-time-specialised loop instead.
77 
80 template<typename CALLABLE>
81 inline void for_glyphs(
82  encoding_group enc, CALLABLE callback, char const buffer[],
83  std::size_t buffer_len, std::size_t start = 0)
84 {
85  auto const scan{get_glyph_scanner(enc)};
86  for (std::size_t here = start, next; here < buffer_len; here = next)
87  {
88  next = scan(buffer, buffer_len, here);
89  PQXX_ASSUME(next > here);
90  callback(buffer + here, buffer + next);
91  }
92 }
93 
94 
95 namespace
96 {
98 constexpr PQXX_PURE unsigned char
99 get_byte(char const buffer[], std::size_t offset) noexcept
100 {
101  return static_cast<unsigned char>(buffer[offset]);
102 }
103 
104 
105 [[noreturn]] PQXX_COLD void throw_for_encoding_error(
106  char const *encoding_name, char const buffer[], std::size_t start,
107  std::size_t count)
108 {
109  std::stringstream s;
110  s << "Invalid byte sequence for encoding " << encoding_name << " at byte "
111  << start << ": " << std::hex << std::setw(2) << std::setfill('0');
112  for (std::size_t i{0}; i < count; ++i)
113  {
114  s << "0x" << static_cast<unsigned int>(get_byte(buffer, start + i));
115  if (i + 1 < count)
116  s << " ";
117  }
118  throw pqxx::argument_error{s.str()};
119 }
120 
121 
123 constexpr PQXX_PURE bool
124 between_inc(unsigned char value, unsigned bottom, unsigned top)
125 {
126  return value >= bottom and value <= top;
127 }
128 } // namespace
129 
130 
132 
136 template<encoding_group> struct glyph_scanner
137 {
138  // TODO: Convert to use string_view?
140  PQXX_PURE static std::size_t
141  call(char const buffer[], std::size_t buffer_len, std::size_t start);
142 };
143 
144 
145 namespace
146 {
148 
154 template<encoding_group ENC, char... NEEDLE>
155 PQXX_PURE inline std::size_t
156 find_ascii_char(std::string_view haystack, std::size_t here)
157 {
158  // We only know how to search for ASCII characters. It's an optimisation
159  // assumption in the code below.
160  static_assert((... and ((NEEDLE & 0x80) == 0)));
161 
162  auto const sz{std::size(haystack)};
163  auto const data{std::data(haystack)};
164  while (here < sz)
165  {
166  // Look up the next character boundary. This can be quite costly, so we
167  // desperately want the call inlined.
168  auto next{glyph_scanner<ENC>::call(data, sz, here)};
169  PQXX_ASSUME(next > here);
170 
171  // (For some reason gcc had a problem with a right-fold here. But clang
172  // was fine.)
173  //
174  // In all supported encodings, if a character's first byte is in the ASCII
175  // range, that means it's a single-byte character. It follows that when we
176  // find a match, we do not need to check that we're in a single-byte
177  // character:
178  //
179  // If this is an "ASCII-unsafe" encoding, e.g. SJIS, we're only checking
180  // each character's first byte. That first byte can only match NEEDLE if
181  // it's a single-byte character.
182  //
183  // In an "ASCII-safe" encoding, e.g. UTF-8 or the ISO-8859 ones, we check
184  // for a match at each byte in the text, because it's faster than finding
185  // character boundaries first. But in these encodings, a multichar byte
186  // never contains any bytes in the ASCII range at all.
187  if ((... or (data[here] == NEEDLE)))
188  return here;
189 
190  // Nope, no hit. Move on.
191  here = next;
192  }
193  return sz;
194 }
195 } // namespace
196 
197 
199 
203 template<encoding_group ENC, char... NEEDLE>
204 PQXX_PURE std::size_t
205 find_s_ascii_char(std::string_view haystack, std::size_t here)
206 {
207  // We only know how to search for ASCII characters. It's an optimisation
208  // assumption in the code below.
209  static_assert((... and ((NEEDLE >> 7) == 0)));
210 
211  auto const sz{std::size(haystack)};
212  auto const data{std::data(haystack)};
213 
214  // No supported encoding has multibyte characters that start with an
215  // ASCII-range byte.
216  while ((... and (data[here] != NEEDLE)))
217  {
218  auto const next = glyph_scanner<ENC>::call(data, sz, here);
219  PQXX_ASSUME(next > here);
220  here = next;
221  }
222  return here;
223 }
224 
225 
227 {
228  static PQXX_PURE constexpr std::size_t
229  call(char const /* buffer */[], std::size_t buffer_len, std::size_t start)
230  {
231  // TODO: Don't bother with npos. Let the caller check.
232  if (start >= buffer_len)
233  PQXX_UNLIKELY return std::string::npos;
234  else
235  return start + 1;
236  }
237 };
238 
239 
240 // https://en.wikipedia.org/wiki/Big5#Organization
241 template<> struct glyph_scanner<encoding_group::BIG5>
242 {
243  static PQXX_PURE std::size_t
244  call(char const buffer[], std::size_t buffer_len, std::size_t start)
245  {
246  if (start >= buffer_len)
247  PQXX_UNLIKELY return std::string::npos;
248 
249  auto const byte1{get_byte(buffer, start)};
250  if (byte1 < 0x80)
251  return start + 1;
252 
253  if (not between_inc(byte1, 0x81, 0xfe) or (start + 2 > buffer_len))
255  throw_for_encoding_error("BIG5", buffer, start, 1);
256 
257  auto const byte2{get_byte(buffer, start + 1)};
258  if (
259  not between_inc(byte2, 0x40, 0x7e) and
260  not between_inc(byte2, 0xa1, 0xfe))
262  throw_for_encoding_error("BIG5", buffer, start, 2);
263 
264  return start + 2;
265  }
266 };
267 
268 
269 /*
270 The PostgreSQL documentation claims that the EUC_* encodings are 1-3 bytes
271 each, but other documents explain that the EUC sets can contain 1-(2,3,4) bytes
272 depending on the specific extension:
273  EUC_CN : 1-2
274  EUC_JP : 1-3
275  EUC_JIS_2004: 1-2
276  EUC_KR : 1-2
277  EUC_TW : 1-4
278 */
279 
280 // https://en.wikipedia.org/wiki/GB_2312#EUC-CN
282 {
283  static PQXX_PURE std::size_t
284  call(char const buffer[], std::size_t buffer_len, std::size_t start)
285  {
286  if (start >= buffer_len)
287  return std::string::npos;
288 
289  auto const byte1{get_byte(buffer, start)};
290  if (byte1 < 0x80)
291  return start + 1;
292 
293  if (not between_inc(byte1, 0xa1, 0xf7) or start + 2 > buffer_len)
295  throw_for_encoding_error("EUC_CN", buffer, start, 1);
296 
297  auto const byte2{get_byte(buffer, start + 1)};
298  if (not between_inc(byte2, 0xa1, 0xfe))
300  throw_for_encoding_error("EUC_CN", buffer, start, 2);
301 
302  return start + 2;
303  }
304 };
305 
306 
307 // EUC-JP and EUC-JIS-2004 represent slightly different code points but iterate
308 // the same:
309 //
310 // https://en.wikipedia.org/wiki/Extended_Unix_Code#EUC-JP
311 // http://x0213.org/codetable/index.en.html
313 {
314  static PQXX_PURE std::size_t
315  call(char const buffer[], std::size_t buffer_len, std::size_t start)
316  {
317  if (start >= buffer_len)
318  return std::string::npos;
319 
320  auto const byte1{get_byte(buffer, start)};
321  if (byte1 < 0x80)
322  return start + 1;
323 
324  if (start + 2 > buffer_len)
326  throw_for_encoding_error("EUC_JP", buffer, start, 1);
327 
328  auto const byte2{get_byte(buffer, start + 1)};
329  if (byte1 == 0x8e)
330  {
331  if (not between_inc(byte2, 0xa1, 0xfe))
333  throw_for_encoding_error("EUC_JP", buffer, start, 2);
334 
335  return start + 2;
336  }
337 
338  if (between_inc(byte1, 0xa1, 0xfe))
339  {
340  if (not between_inc(byte2, 0xa1, 0xfe))
342  throw_for_encoding_error("EUC_JP", buffer, start, 2);
343 
344  return start + 2;
345  }
346 
347  if (byte1 == 0x8f and start + 3 <= buffer_len)
348  {
349  auto const byte3{get_byte(buffer, start + 2)};
350  if (
351  not between_inc(byte2, 0xa1, 0xfe) or
352  not between_inc(byte3, 0xa1, 0xfe))
354  throw_for_encoding_error("EUC_JP", buffer, start, 3);
355 
356  return start + 3;
357  }
358 
359  throw_for_encoding_error("EUC_JP", buffer, start, 1);
360  }
361 };
362 
363 
364 // https://en.wikipedia.org/wiki/Extended_Unix_Code#EUC-KR
366 {
367  static PQXX_PURE std::size_t
368  call(char const buffer[], std::size_t buffer_len, std::size_t start)
369  {
370  if (start >= buffer_len)
371  PQXX_UNLIKELY return std::string::npos;
372 
373  auto const byte1{get_byte(buffer, start)};
374  if (byte1 < 0x80)
375  return start + 1;
376 
377  if (not between_inc(byte1, 0xa1, 0xfe) or start + 2 > buffer_len)
379  throw_for_encoding_error("EUC_KR", buffer, start, 1);
380 
381  auto const byte2{get_byte(buffer, start + 1)};
382  if (not between_inc(byte2, 0xa1, 0xfe))
384  throw_for_encoding_error("EUC_KR", buffer, start, 1);
385 
386  return start + 2;
387  }
388 };
389 
390 
391 // https://en.wikipedia.org/wiki/Extended_Unix_Code#EUC-TW
393 {
394  static PQXX_PURE std::size_t
395  call(char const buffer[], std::size_t buffer_len, std::size_t start)
396  {
397  if (start >= buffer_len)
399  return std::string::npos;
400 
401  auto const byte1{get_byte(buffer, start)};
402  if (byte1 < 0x80)
403  return start + 1;
404 
405  if (start + 2 > buffer_len)
407  throw_for_encoding_error("EUC_KR", buffer, start, 1);
408 
409  auto const byte2{get_byte(buffer, start + 1)};
410  if (between_inc(byte1, 0xa1, 0xfe))
411  {
412  if (not between_inc(byte2, 0xa1, 0xfe))
414  throw_for_encoding_error("EUC_KR", buffer, start, 2);
415 
416  return start + 2;
417  }
418 
419  if (byte1 != 0x8e or start + 4 > buffer_len)
421  throw_for_encoding_error("EUC_KR", buffer, start, 1);
422 
423  if (
424  between_inc(byte2, 0xa1, 0xb0) and
425  between_inc(get_byte(buffer, start + 2), 0xa1, 0xfe) and
426  between_inc(get_byte(buffer, start + 3), 0xa1, 0xfe))
427  return start + 4;
428 
430  throw_for_encoding_error("EUC_KR", buffer, start, 4);
431  }
432 };
433 
434 
435 // https://en.wikipedia.org/wiki/GB_18030#Mapping
437 {
438  static PQXX_PURE std::size_t
439  call(char const buffer[], std::size_t buffer_len, std::size_t start)
440  {
441  if (start >= buffer_len)
442  PQXX_UNLIKELY return std::string::npos;
443 
444  auto const byte1{get_byte(buffer, start)};
445  if (byte1 < 0x80)
446  return start + 1;
447  if (byte1 == 0x80)
448  throw_for_encoding_error("GB18030", buffer, start, buffer_len - start);
449 
450  if (start + 2 > buffer_len)
452  throw_for_encoding_error("GB18030", buffer, start, buffer_len - start);
453 
454  auto const byte2{get_byte(buffer, start + 1)};
455  if (between_inc(byte2, 0x40, 0xfe))
456  {
457  if (byte2 == 0x7f)
459  throw_for_encoding_error("GB18030", buffer, start, 2);
460 
461  return start + 2;
462  }
463 
464  if (start + 4 > buffer_len)
466  throw_for_encoding_error("GB18030", buffer, start, buffer_len - start);
467 
468  if (
469  between_inc(byte2, 0x30, 0x39) and
470  between_inc(get_byte(buffer, start + 2), 0x81, 0xfe) and
471  between_inc(get_byte(buffer, start + 3), 0x30, 0x39))
472  return start + 4;
473 
475  throw_for_encoding_error("GB18030", buffer, start, 4);
476  }
477 };
478 
479 
480 // https://en.wikipedia.org/wiki/GBK_(character_encoding)#Encoding
481 template<> struct glyph_scanner<encoding_group::GBK>
482 {
483  static PQXX_PURE std::size_t
484  call(char const buffer[], std::size_t buffer_len, std::size_t start)
485  {
486  if (start >= buffer_len)
487  PQXX_UNLIKELY return std::string::npos;
488 
489  auto const byte1{get_byte(buffer, start)};
490  if (byte1 < 0x80)
491  return start + 1;
492 
493  if (start + 2 > buffer_len)
495  throw_for_encoding_error("GBK", buffer, start, 1);
496 
497  auto const byte2{get_byte(buffer, start + 1)};
498  if (
499  (between_inc(byte1, 0xa1, 0xa9) and between_inc(byte2, 0xa1, 0xfe)) or
500  (between_inc(byte1, 0xb0, 0xf7) and between_inc(byte2, 0xa1, 0xfe)) or
501  (between_inc(byte1, 0x81, 0xa0) and between_inc(byte2, 0x40, 0xfe) and
502  byte2 != 0x7f) or
503  (between_inc(byte1, 0xaa, 0xfe) and between_inc(byte2, 0x40, 0xa0) and
504  byte2 != 0x7f) or
505  (between_inc(byte1, 0xa8, 0xa9) and between_inc(byte2, 0x40, 0xa0) and
506  byte2 != 0x7f) or
507  (between_inc(byte1, 0xaa, 0xaf) and between_inc(byte2, 0xa1, 0xfe)) or
508  (between_inc(byte1, 0xf8, 0xfe) and between_inc(byte2, 0xa1, 0xfe)) or
509  (between_inc(byte1, 0xa1, 0xa7) and between_inc(byte2, 0x40, 0xa0) and
510  byte2 != 0x7f))
511  return start + 2;
512 
514  throw_for_encoding_error("GBK", buffer, start, 2);
515  }
516 };
517 
518 
519 /*
520 The PostgreSQL documentation claims that the JOHAB encoding is 1-3 bytes, but
521 "CJKV Information Processing" describes it (actually just the Hangul portion)
522 as "three five-bit segments" that reside inside 16 bits (2 bytes).
523 
524 CJKV Information Processing by Ken Lunde, pg. 269:
525 
526  https://bit.ly/2BEOu5V
527 */
528 template<> struct glyph_scanner<encoding_group::JOHAB>
529 {
530  static PQXX_PURE std::size_t
531  call(char const buffer[], std::size_t buffer_len, std::size_t start)
532  {
533  if (start >= buffer_len)
534  PQXX_UNLIKELY return std::string::npos;
535 
536  auto const byte1{get_byte(buffer, start)};
537  if (byte1 < 0x80)
538  return start + 1;
539 
540  if (start + 2 > buffer_len)
542  throw_for_encoding_error("JOHAB", buffer, start, 1);
543 
544  auto const byte2{get_byte(buffer, start)};
545  if (
546  (between_inc(byte1, 0x84, 0xd3) and
547  (between_inc(byte2, 0x41, 0x7e) or between_inc(byte2, 0x81, 0xfe))) or
548  ((between_inc(byte1, 0xd8, 0xde) or between_inc(byte1, 0xe0, 0xf9)) and
549  (between_inc(byte2, 0x31, 0x7e) or between_inc(byte2, 0x91, 0xfe))))
550  return start + 2;
551 
553  throw_for_encoding_error("JOHAB", buffer, start, 2);
554  }
555 };
556 
557 
558 /*
559 PostgreSQL's MULE_INTERNAL is the emacs rather than Xemacs implementation;
560 see the server/mb/pg_wchar.h PostgreSQL header file.
561 This is implemented according to the description in said header file, but I was
562 unable to get it to successfully iterate a MULE-encoded test CSV generated
563 using PostgreSQL 9.2.23. Use this at your own risk.
564 */
566 {
567  static PQXX_PURE std::size_t
568  call(char const buffer[], std::size_t buffer_len, std::size_t start)
569  {
570  if (start >= buffer_len)
571  PQXX_UNLIKELY return std::string::npos;
572 
573  auto const byte1{get_byte(buffer, start)};
574  if (byte1 < 0x80)
575  return start + 1;
576 
577  if (start + 2 > buffer_len)
579  throw_for_encoding_error("MULE_INTERNAL", buffer, start, 1);
580 
581  auto const byte2{get_byte(buffer, start + 1)};
582  if (between_inc(byte1, 0x81, 0x8d) and byte2 >= 0xa0)
583  return start + 2;
584 
585  if (start + 3 > buffer_len)
587  throw_for_encoding_error("MULE_INTERNAL", buffer, start, 2);
588 
589  if (
590  ((byte1 == 0x9a and between_inc(byte2, 0xa0, 0xdf)) or
591  (byte1 == 0x9b and between_inc(byte2, 0xe0, 0xef)) or
592  (between_inc(byte1, 0x90, 0x99) and byte2 >= 0xa0)) and
593  (byte2 >= 0xa0))
594  return start + 3;
595 
596  if (start + 4 > buffer_len)
598  throw_for_encoding_error("MULE_INTERNAL", buffer, start, 3);
599 
600  if (
601  ((byte1 == 0x9c and between_inc(byte2, 0xf0, 0xf4)) or
602  (byte1 == 0x9d and between_inc(byte2, 0xf5, 0xfe))) and
603  get_byte(buffer, start + 2) >= 0xa0 and
604  get_byte(buffer, start + 4) >= 0xa0)
605  return start + 4;
606 
608  throw_for_encoding_error("MULE_INTERNAL", buffer, start, 4);
609  }
610 };
611 
612 
613 // As far as I can tell, for the purposes of iterating the only difference
614 // between SJIS and SJIS-2004 is increased range in the first byte of two-byte
615 // sequences (0xEF increased to 0xFC). Officially, that is; apparently the
616 // version of SJIS used by Postgres has the same range as SJIS-2004. They both
617 // have increased range over the documented versions, not having the even/odd
618 // restriction for the first byte in 2-byte sequences.
619 //
620 // https://en.wikipedia.org/wiki/Shift_JIS#Shift_JIS_byte_map
621 // http://x0213.org/codetable/index.en.html
622 template<> struct glyph_scanner<encoding_group::SJIS>
623 {
624  static PQXX_PURE std::size_t
625  call(char const buffer[], std::size_t buffer_len, std::size_t start)
626  {
627  if (start >= buffer_len)
628  return std::string::npos;
629 
630  auto const byte1{get_byte(buffer, start)};
631  if (byte1 < 0x80 or between_inc(byte1, 0xa1, 0xdf))
632  return start + 1;
633 
634  if (
635  not between_inc(byte1, 0x81, 0x9f) and
636  not between_inc(byte1, 0xe0, 0xfc))
638  throw_for_encoding_error("SJIS", buffer, start, 1);
639 
640  if (start + 2 > buffer_len)
642  throw_for_encoding_error("SJIS", buffer, start, buffer_len - start);
643 
644  auto const byte2{get_byte(buffer, start + 1)};
645  if (byte2 == 0x7f)
647  throw_for_encoding_error("SJIS", buffer, start, 2);
648 
649  if (between_inc(byte2, 0x40, 0x9e) or between_inc(byte2, 0x9f, 0xfc))
650  return start + 2;
651 
653  throw_for_encoding_error("SJIS", buffer, start, 2);
654  }
655 };
656 
657 
658 // https://en.wikipedia.org/wiki/Unified_Hangul_Code
659 template<> struct glyph_scanner<encoding_group::UHC>
660 {
661  static PQXX_PURE std::size_t
662  call(char const buffer[], std::size_t buffer_len, std::size_t start)
663  {
664  if (start >= buffer_len)
665  PQXX_UNLIKELY return std::string::npos;
666 
667  auto const byte1{get_byte(buffer, start)};
668  if (byte1 < 0x80)
669  return start + 1;
670 
671  if (start + 2 > buffer_len)
673  throw_for_encoding_error("UHC", buffer, start, buffer_len - start);
674 
675  auto const byte2{get_byte(buffer, start + 1)};
676  if (between_inc(byte1, 0x80, 0xc6))
677  {
678  if (
679  between_inc(byte2, 0x41, 0x5a) or between_inc(byte2, 0x61, 0x7a) or
680  between_inc(byte2, 0x80, 0xfe))
681  return start + 2;
682 
684  throw_for_encoding_error("UHC", buffer, start, 2);
685  }
686 
687  if (between_inc(byte1, 0xa1, 0xfe))
688  {
689  if (not between_inc(byte2, 0xa1, 0xfe))
691  throw_for_encoding_error("UHC", buffer, start, 2);
692 
693  return start + 2;
694  }
695 
696  throw_for_encoding_error("UHC", buffer, start, 1);
697  }
698 };
699 
700 
701 // https://en.wikipedia.org/wiki/UTF-8#Description
702 template<> struct glyph_scanner<encoding_group::UTF8>
703 {
704  static PQXX_PURE std::size_t
705  call(char const buffer[], std::size_t buffer_len, std::size_t start)
706  {
707  if (start >= buffer_len)
708  PQXX_UNLIKELY return std::string::npos;
709 
710  auto const byte1{get_byte(buffer, start)};
711  if (byte1 < 0x80)
712  return start + 1;
713 
714  if (start + 2 > buffer_len)
716  throw_for_encoding_error("UTF8", buffer, start, buffer_len - start);
717 
718  auto const byte2{get_byte(buffer, start + 1)};
719  if (between_inc(byte1, 0xc0, 0xdf))
720  {
721  if (not between_inc(byte2, 0x80, 0xbf))
723  throw_for_encoding_error("UTF8", buffer, start, 2);
724 
725  return start + 2;
726  }
727 
728  if (start + 3 > buffer_len)
730  throw_for_encoding_error("UTF8", buffer, start, buffer_len - start);
731 
732  auto const byte3{get_byte(buffer, start + 2)};
733  if (between_inc(byte1, 0xe0, 0xef))
734  {
735  if (between_inc(byte2, 0x80, 0xbf) and between_inc(byte3, 0x80, 0xbf))
736  return start + 3;
737 
739  throw_for_encoding_error("UTF8", buffer, start, 3);
740  }
741 
742  if (start + 4 > buffer_len)
744  throw_for_encoding_error("UTF8", buffer, start, buffer_len - start);
745 
746  if (between_inc(byte1, 0xf0, 0xf7))
747  {
748  if (
749  between_inc(byte2, 0x80, 0xbf) and between_inc(byte3, 0x80, 0xbf) and
750  between_inc(get_byte(buffer, start + 3), 0x80, 0xbf))
751  return start + 4;
752 
754  throw_for_encoding_error("UTF8", buffer, start, 4);
755  }
756 
758  throw_for_encoding_error("UTF8", buffer, start, 1);
759  }
760 };
761 
762 
764 
778 constexpr inline encoding_group
780 {
781  switch (enc)
782  {
790  // All these encodings are "ASCII-safe," meaning that if we're looking
791  // for a particular ASCII character, we can safely just go through the
792  // string byte for byte. Multibyte characters have the high bit set.
794 
795  default: PQXX_UNLIKELY return enc;
796  }
797 }
798 
799 
801 
807 template<char... NEEDLE>
808 PQXX_PURE constexpr inline char_finder_func *
810 {
811  auto const as_if{map_ascii_search_group(enc)};
812  switch (as_if)
813  {
815  return pqxx::internal::find_ascii_char<
816  encoding_group::MONOBYTE, NEEDLE...>;
818  return pqxx::internal::find_ascii_char<encoding_group::BIG5, NEEDLE...>;
820  return pqxx::internal::find_ascii_char<encoding_group::GB18030, NEEDLE...>;
821  case encoding_group::GBK:
822  return pqxx::internal::find_ascii_char<encoding_group::GBK, NEEDLE...>;
824  return pqxx::internal::find_ascii_char<encoding_group::JOHAB, NEEDLE...>;
826  return pqxx::internal::find_ascii_char<encoding_group::SJIS, NEEDLE...>;
827  case encoding_group::UHC:
828  return pqxx::internal::find_ascii_char<encoding_group::UHC, NEEDLE...>;
829 
830  default:
832  "Unexpected encoding group: ",
833  static_cast<std::underlying_type_t<encoding_group>>(as_if),
834  " (mapped from ",
835  static_cast<std::underlying_type_t<encoding_group>>(enc), ").")};
836  }
837 }
838 
839 
841 
844 template<char... NEEDLE>
845 PQXX_PURE constexpr inline char_finder_func *
847 {
848  auto const as_if{map_ascii_search_group(enc)};
849  switch (as_if)
850  {
853  encoding_group::MONOBYTE, NEEDLE...>;
858  encoding_group::GB18030, NEEDLE...>;
859  case encoding_group::GBK:
865  case encoding_group::UHC:
867 
868  default:
870  "Unexpected encoding group: ",
871  static_cast<std::underlying_type_t<encoding_group>>(as_if),
872  " (mapped from ",
873  static_cast<std::underlying_type_t<encoding_group>>(enc), ").")};
874  }
875 }
876 } // namespace pqxx::internal
877 #endif
static PQXX_PURE std::size_t call(char const buffer[], std::size_t buffer_len, std::size_t start)
Definition: encodings.hxx:705
static PQXX_PURE std::size_t call(char const buffer[], std::size_t buffer_len, std::size_t start)
Find the next glyph in buffer after position start.
std::string concat(TYPE...item)
Efficiently combine a bunch of items into one big string.
Definition: concat.hxx:31
#define PQXX_ASSUME(condition)
Definition: header-pre.hxx:187
static PQXX_PURE std::size_t call(char const buffer[], std::size_t buffer_len, std::size_t start)
Definition: encodings.hxx:568
#define PQXX_PURE
Declare function "pure": no side effects, only reads globals and its args.
Definition: header-pre.hxx:77
encoding_group
Definition: encoding_group.hxx:18
void for_glyphs(encoding_group enc, CALLABLE callback, char const buffer[], std::size_t buffer_len, std::size_t start=0)
Iterate over the glyphs in a buffer.
Definition: encodings.hxx:81
Internal items for libpqxx' own use. Do not use these yourself.
Definition: encodings.cxx:32
#define PQXX_COLD
Definition: header-pre.hxx:87
PQXX_PURE constexpr char_finder_func * get_char_finder(encoding_group enc)
Look up a character search function for an encoding group.
Definition: encodings.hxx:809
static PQXX_PURE std::size_t call(char const buffer[], std::size_t buffer_len, std::size_t start)
Definition: encodings.hxx:662
static PQXX_PURE std::size_t call(char const buffer[], std::size_t buffer_len, std::size_t start)
Definition: encodings.hxx:531
PQXX_PURE constexpr char_finder_func * get_s_char_finder(encoding_group enc)
Look up a "sentry" character search function for an encoding group.
Definition: encodings.hxx:846
static PQXX_PURE std::size_t call(char const buffer[], std::size_t buffer_len, std::size_t start)
Definition: encodings.hxx:315
Wrapper struct template for "find next glyph" functions.
Definition: encodings.hxx:136
static PQXX_PURE std::size_t call(char const buffer[], std::size_t buffer_len, std::size_t start)
Definition: encodings.hxx:625
static PQXX_PURE std::size_t call(char const buffer[], std::size_t buffer_len, std::size_t start)
Definition: encodings.hxx:484
static PQXX_PURE constexpr std::size_t call(char const [], std::size_t buffer_len, std::size_t start)
Definition: encodings.hxx:229
#define PQXX_LIBEXPORT
Definition: header-pre.hxx:157
static PQXX_PURE std::size_t call(char const buffer[], std::size_t buffer_len, std::size_t start)
Definition: encodings.hxx:368
#define PQXX_UNLIKELY
Definition: header-pre.hxx:178
static PQXX_PURE std::size_t call(char const buffer[], std::size_t buffer_len, std::size_t start)
Definition: encodings.hxx:395
Invalid argument passed to libpqxx, similar to std::invalid_argument.
Definition: except.hxx:265
std::size_t(char const buffer[], std::size_t buffer_len, std::size_t start) glyph_scanner_func
Function type: "find the end of the current glyph.".
Definition: encoding_group.hxx:53
constexpr encoding_group map_ascii_search_group(encoding_group enc) noexcept
Just for searching an ASCII character, what encoding can we use here?
Definition: encodings.hxx:779
std::size_t find_char(glyph_scanner_func *scanner, std::string_view haystack, std::size_t here=0u)
Find any of the ASCII characters NEEDLE in haystack.
Definition: encodings.hxx:46
std::size_t(std::string_view haystack, std::size_t start) char_finder_func
Function type: "find first occurrence of specific any of ASCII characters.".
Definition: encoding_group.hxx:71
pqxx::internal::encoding_group enc_group(std::string_view encoding_name)
Convert libpq encoding name to its libpqxx encoding group.
Definition: encodings.cxx:35
Internal error in libpqxx library.
Definition: except.hxx:241
PQXX_PURE std::size_t find_s_ascii_char(std::string_view haystack, std::size_t here)
Find first of NEEDLE ASCII chars in haystack.
Definition: encodings.hxx:205
static PQXX_PURE std::size_t call(char const buffer[], std::size_t buffer_len, std::size_t start)
Definition: encodings.hxx:439
PQXX_LIBEXPORT glyph_scanner_func * get_glyph_scanner(encoding_group)
Look up the glyph scanner function for a given encoding group.
PQXX_PURE char const * name_encoding(int encoding_id)
Return PostgreSQL's name for encoding enum value.
static PQXX_PURE std::size_t call(char const buffer[], std::size_t buffer_len, std::size_t start)
Definition: encodings.hxx:244
static PQXX_PURE std::size_t call(char const buffer[], std::size_t buffer_len, std::size_t start)
Definition: encodings.hxx:284