aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/libpqxx/src/binarystring.cxx
blob: 0091f48bafc6223c23005363e6887b1a1f50281e (plain) (blame)
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
/** Implementation of bytea (binary string) conversions.
 *
 * Copyright (c) 2000-2019, Jeroen T. Vermeulen.
 *
 * See COPYING for copyright license.  If you did not receive a file called
 * COPYING with this source code, please notify the distributor of this mistake,
 * or contact the author.
 */
#include "pqxx/compiler-internal.hxx"

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <new>
#include <stdexcept>

extern "C"
{
#include "libpq-fe.h"
}

#include "pqxx/binarystring"
#include "pqxx/field"


using namespace pqxx::internal;

namespace
{
using unsigned_char = unsigned char;
using buffer = std::pair<unsigned char *, size_t>;


buffer to_buffer(const void *data, size_t len)
{
  void *const output{malloc(len + 1)};
  if (output == nullptr) throw std::bad_alloc{};
  static_cast<char *>(output)[len] = '\0';
  memcpy(static_cast<char *>(output), data, len);
  return buffer{static_cast<unsigned char *>(output), len};
}


buffer to_buffer(const std::string &source)
{
  return to_buffer(source.c_str(), source.size());
}



buffer unescape(const unsigned char escaped[])
{
#ifdef _WIN32
  /* On Windows only, the return value from PQunescapeBytea() must be freed
   * using PQfreemem.  Copy to a buffer allocated by libpqxx, so that the
   * binarystring's buffer can be freed uniformly,
   */
  size_t unescaped_len = 0;
  // TODO: Use make_unique once we require C++14.  Sooo much easier.
  std::unique_ptr<unsigned char, void(*)(unsigned char *)> A(
	PQunescapeBytea(const_cast<unsigned char *>(escaped), &unescaped_len),
	freepqmem_templated<unsigned char>);
  void *data = A.get();
  if (data == nullptr) throw std::bad_alloc{};
  return to_buffer(data, unescaped_len);
#else
  /* On non-Windows platforms, it's okay to free libpq-allocated memory using
   * free().  No extra copy needed.
   */
  buffer unescaped;
  unescaped.first = PQunescapeBytea(
	const_cast<unsigned char *>(escaped), &unescaped.second);
  if (unescaped.first == nullptr) throw std::bad_alloc{};
  return unescaped;
#endif
}

} // namespace


pqxx::binarystring::binarystring(const field &F) :
  m_buf{make_smart_pointer()},
  m_size{0}
{
  buffer unescaped{unescape(reinterpret_cast<const_pointer>(F.c_str()))};
  m_buf = make_smart_pointer(unescaped.first);
  m_size = unescaped.second;
}


pqxx::binarystring::binarystring(const std::string &s) :
  m_buf{make_smart_pointer()},
  m_size{s.size()}
{
  m_buf = make_smart_pointer(to_buffer(s).first);
}


pqxx::binarystring::binarystring(const void *binary_data, size_t len) :
  m_buf{make_smart_pointer()},
  m_size{len}
{
  m_buf = make_smart_pointer(to_buffer(binary_data, len).first);
}


bool pqxx::binarystring::operator==(const binarystring &rhs) const noexcept
{
  if (rhs.size() != size()) return false;
  return std::memcmp(data(), rhs.data(), size()) == 0;
}


pqxx::binarystring &pqxx::binarystring::operator=(const binarystring &rhs)
{
  m_buf = rhs.m_buf;
  m_size = rhs.m_size;
  return *this;
}


pqxx::binarystring::const_reference pqxx::binarystring::at(size_type n) const
{
  if (n >= m_size)
  {
    if (m_size == 0)
      throw std::out_of_range{"Accessing empty binarystring"};
    throw std::out_of_range{
	"binarystring index out of range: " +
	to_string(n) + " (should be below " + to_string(m_size) + ")"};
  }
  return data()[n];
}


void pqxx::binarystring::swap(binarystring &rhs)
{
  m_buf.swap(rhs.m_buf);

  // This part very obviously can't go wrong, so do it last
  const auto s = m_size;
  m_size = rhs.m_size;
  rhs.m_size = s;
}


std::string pqxx::binarystring::str() const
{
  return std::string{get(), m_size};
}