fix
[libpgclient.git] / src / pgfld.c
blob3d9f35e1b691b68f26151701b828dd3f0d2e879d
1 /*Copyright (c) Brian B.
3 This library is free software; you can redistribute it and/or
4 modify it under the terms of the GNU Lesser General Public
5 License as published by the Free Software Foundation; either
6 version 3 of the License, or (at your option) any later version.
7 See the file LICENSE included with this distribution for more
8 information.
9 */
10 #include "libpgcli/pgfld.h"
12 //******************************************************************************
13 // Time functions
14 //******************************************************************************
15 #define MONTHS_PER_YEAR 12
16 #define POSTGRES_EPOCH_JDATE 2451545
17 #define INT64CONST(x) (x##L)
18 #define SECS_PER_YEAR (36525 * 864)
19 #define SECS_PER_DAY 86400
20 #define SECS_PER_HOUR 3600
21 #define SECS_PER_MINUTE 60
22 #define MINS_PER_HOUR 60
23 #define USECS_PER_DAY INT64CONST(86400000000)
24 #define USECS_PER_HOUR INT64CONST(3600000000)
25 #define USECS_PER_MINUTE INT64CONST(60000000)
26 #define USECS_PER_SEC INT64CONST(1000000)
27 #define MIN_TIMESTAMP INT64CONST(-211813488000000000)
28 #define END_TIMESTAMP INT64CONST(9223371331200000000)
29 #define IS_VALID_TIMESTAMP(t) (MIN_TIMESTAMP <= (t) && (t) < END_TIMESTAMP)
30 #define TMODULO(t,q,u) \
31 do { \
32 (q) = ((t) / (u)); \
33 if ((q) != 0) (t) -= ((q) * (u)); \
34 } while(0)
35 #define JULIAN_MINYEAR (-4713)
36 #define JULIAN_MINMONTH (11)
37 #define JULIAN_MINDAY (24)
38 #define JULIAN_MAXYEAR (5874898)
39 #define JULIAN_MAXMONTH (6)
40 #define JULIAN_MAXDAY (3)
41 #define IS_VALID_JULIAN(y,m,d) \
42 (((y) > JULIAN_MINYEAR || \
43 ((y) == JULIAN_MINYEAR && ((m) >= JULIAN_MINMONTH))) && \
44 ((y) < JULIAN_MAXYEAR || \
45 ((y) == JULIAN_MAXYEAR && ((m) < JULIAN_MAXMONTH))))
47 void j2date(int jd, int *year, int *month, int *day) {
48 unsigned int julian;
49 unsigned int quad;
50 unsigned int extra;
51 int y;
52 julian = jd;
53 julian += 32044;
54 quad = julian / 146097;
55 extra = (julian - quad * 146097) * 4 + 3;
56 julian += 60 + quad * 3 + extra / 146097;
57 quad = julian / 1461;
58 julian -= quad * 1461;
59 y = julian * 4 / 1461;
60 julian = ((y != 0) ? ((julian + 305) % 365) : ((julian + 306) % 366)) + 123;
61 y += quad * 4;
62 *year = y - 4800;
63 quad = julian * 2141 / 65536;
64 *day = julian - 7834 * quad / 256;
65 *month = (quad + 10) % MONTHS_PER_YEAR + 1;
68 int date2j(int y, int m, int d) {
69 int julian;
70 int century;
71 if (m > 2) {
72 m += 1;
73 y += 4800;
74 } else {
75 m += 13;
76 y += 4799;
78 century = y / 100;
79 julian = y * 365 - 32167;
80 julian += y / 4 - century + century / 4;
81 julian += 7834 * m / 256 + d;
83 return julian;
86 static void dt2time(int64_t jd, int *hour, int *min, int *sec) {
87 tm_t time;
88 time = jd;
89 *hour = time / USECS_PER_HOUR;
90 time -= (*hour) * USECS_PER_HOUR;
91 *min = time / USECS_PER_MINUTE;
92 time -= (*min) * USECS_PER_MINUTE;
93 *sec = time / USECS_PER_SEC;
96 int timestamp_to_tm (tm_t dt, struct tm *tm) {
97 tm_t date, time = dt;
98 memset(tm, 0, sizeof(struct tm));
99 TMODULO(time, date, USECS_PER_DAY);
100 if (time < INT64CONST(0)) {
101 time += USECS_PER_DAY;
102 date -= 1;
104 date += POSTGRES_EPOCH_JDATE;
105 if (date < 0 || date > (int64_t)INT_MAX)
106 return -1;
107 j2date((int)date, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
108 dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec);
109 return 0;
112 static int64_t time2t(const int hour, const int min, const int sec, const fsec_t fsec) {
113 return (((((hour * MINS_PER_HOUR) + min) * SECS_PER_MINUTE) + sec) * USECS_PER_SEC) + fsec;
116 static tm_t dt2local(tm_t dt, int tz) {
117 dt -= (tz * USECS_PER_SEC);
118 return dt;
121 int tm_to_timestamp (struct tm *tm, fsec_t fsec, int *tzp, tm_t *result) {
122 int dDate;
123 int64_t time;
124 if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
125 return -1;
126 dDate = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
127 time = time2t(tm->tm_hour, tm->tm_min, tm->tm_sec, fsec);
128 *result = (dDate * USECS_PER_DAY) + time;
129 if ((*result - time) / USECS_PER_DAY != dDate)
130 return -1;
131 if ((*result < 0 && dDate > 0) ||
132 (*result > 0 && dDate < -1))
133 return -1;
134 if (tzp != NULL)
135 *result = dt2local(*result, -(*tzp));
136 if (!IS_VALID_TIMESTAMP(*result))
137 return -1;
138 return 0;
142 struct tm *date_to_tm (date_t dt, struct tm *tm) {
143 memset(tm, 0, sizeof(struct tm));
144 j2date(dt + POSTGRES_EPOCH_JDATE, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
145 return tm;
148 date_t tm_to_date (struct tm *tm) {
149 date_t dt = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
150 return dt - POSTGRES_EPOCH_JDATE;
153 struct tm *interval_to_tm(pg_intv_t *span, struct tm *tm) {
154 tm_t time;
155 if (span->month != 0) {
156 tm->tm_year = span->month / MONTHS_PER_YEAR;
157 tm->tm_mon = span->month % MONTHS_PER_YEAR;
158 } else {
159 tm->tm_year = 0;
160 tm->tm_mon = 0;
162 time = span->time;
163 tm->tm_mday = time / USECS_PER_DAY;
164 time -= tm->tm_mday * USECS_PER_DAY;
165 tm->tm_hour = time / USECS_PER_HOUR;
166 time -= tm->tm_hour * USECS_PER_HOUR;
167 tm->tm_min = time / USECS_PER_MINUTE;
168 time -= tm->tm_min * USECS_PER_MINUTE;
169 tm->tm_sec = time / USECS_PER_SEC;
170 return tm;
173 //******************************************************************************
174 // Double function
175 //******************************************************************************
176 double pg_conv_double (double d) {
177 union {
178 double d;
179 unsigned char bytes [8];
180 } src, dst;
181 src.d = d;
182 dst.bytes[0] = src.bytes[7];
183 dst.bytes[1] = src.bytes[6];
184 dst.bytes[2] = src.bytes[5];
185 dst.bytes[3] = src.bytes[4];
186 dst.bytes[4] = src.bytes[3];
187 dst.bytes[5] = src.bytes[2];
188 dst.bytes[6] = src.bytes[1];
189 dst.bytes[7] = src.bytes[0];
190 return dst.d;
193 float pg_conv_float (float f) {
194 union {
195 float f;
196 unsigned char bytes [4];
197 } src, dst;
198 src.f = f;
199 dst.bytes[0] = src.bytes[3];
200 dst.bytes[1] = src.bytes[2];
201 dst.bytes[2] = src.bytes[1];
202 dst.bytes[3] = src.bytes[0];
203 return dst.f;
206 //******************************************************************************
207 // Numeric functions
208 //******************************************************************************
209 #ifdef HAVE_GMP
210 #define MAX_DIGITS (sizeof(mp_limb_t) / sizeof(int16_t))
211 static void set_digits_n (mpz_t x, int16_t *p_digits, int16_t *digits, int len, int weight) {
212 int i = 1;
213 mpz_t m;
214 mpz_init_set_ui(m, 1);
215 if (p_digits < digits)
216 mpz_set_ui(x, 0);
217 else
218 mpz_set_ui(x, *p_digits);
219 p_digits++;
220 if (len < weight) {
221 while (i < len) {
222 if (p_digits < digits)
223 mpz_mul_si(m, m, 10000);
224 else
225 mpz_set_ui(m, 10000);
226 mpz_mul(x, x, m);
227 mpz_add_ui(x, x, *p_digits++);
228 i++;
230 while (i < weight) {
231 mpz_mul_si(x, x, 10000);
232 ++i;
234 } else {
235 while (i < weight) {
236 if (p_digits < digits)
237 mpz_mul_si(m, m, 10000);
238 else
239 mpz_set_ui(m, 10000);
240 mpz_mul(x, x, m);
241 mpz_add_ui(x, x, *p_digits++);
242 i++;
245 mpz_clear(m);
248 static void set_digits_d (mpz_t x, int count) {
249 int i = 0;
250 while (i < count) {
251 mpz_mul_si(x, x, 10000);
252 i++;
256 void pg_get_numeric (uint8_t *buf, mpq_t res, uint16_t *scale) {
257 int16_t len = be16toh(*(int16_t*)buf); buf += sizeof(int16_t);
258 int16_t weight = be16toh(*(int16_t*)buf); buf += sizeof(int16_t);
259 int16_t sign = be16toh(*(int16_t*)buf); buf += sizeof(int16_t);
260 mpq_init(res);
261 if (sign == NUMERIC_POS || sign == NUMERIC_NEG || sign == NUMERIC_NAN) {
262 uint16_t dscale = be16toh(*(uint16_t*)buf); buf += sizeof(uint16_t);
263 if (scale)
264 *scale = dscale;
265 if ((dscale & NUMERIC_DSCALE_MASK) == dscale) {
266 if (0 == len)
267 return;
268 mpq_t x, y;
269 mpq_ptr q = x;
270 mpz_ptr n = &q->_mp_num, d = &q->_mp_den;
271 int16_t *digits = malloc(len * sizeof(int16_t)), *p_digits = digits;
272 for (int i = 0; i < len; ++i)
273 digits[i] = be16toh(*(int16_t*)buf);
274 buf += sizeof(int16_t);
275 ++weight;
276 mpz_init(n);
277 if (weight > 0)
278 set_digits_n(n, p_digits, digits, len, weight);
279 mpz_init(d);
280 mpz_set_si(d, 1);
281 int cnt = len - weight;
282 if (cnt <= 0)
283 mpq_set(res, q);
284 else {
285 p_digits += weight;
286 q = y;
287 n = &q->_mp_num;
288 d = &q->_mp_den;
289 mpz_init(n);
290 mpz_init_set_si(d, 1);
291 set_digits_n(n, p_digits, digits, cnt, cnt);
292 set_digits_d(d, cnt);
293 mpq_add(res, x, y);
294 mpq_clear(y);
296 if (sign == NUMERIC_NEG)
297 mpq_neg(res, res);
298 free(digits);
299 mpq_clear(x);
302 return;
305 str_t *pg_numstr (mpq_ptr x, int base, uint16_t dscale, int float_prec) {
306 char *s;
307 mpf_t f;
308 mp_exp_t exp;
309 if (0 == dscale) {
310 mpz_ptr z = &x->_mp_num;
311 if (!(s = mpz_get_str(NULL, base, z)))
312 return NULL;
313 str_t *res = mkstr(s, strlen(s), 8);
314 free(s);
315 return res;
317 mpf_init2(f, float_prec);
318 mpf_set_q(f, x);
319 if ((s = mpf_get_str(NULL, &exp, base, 0, f))) {
320 str_t *res;
321 ssize_t n;
322 char *e, *p;
323 if (exp > 0) {
324 res = mkstr(s, strlen(s), 8);
325 if (exp > res->len) {
326 strpad(&res, exp, '0', STR_LEFT);
327 strnadd(&res, CONST_STR_LEN(".0"));
328 } else
329 strepl(&res, res->ptr + exp, 0, CONST_STR_LEN("."));
330 } else {
331 res = mkstr(CONST_STR_LEN("0."), 8);
332 if (exp < 0)
333 strpad(&res, -(exp-2), '0', STR_LEFT);
334 strnadd(&res, s, strlen(s));
335 exp = 1;
337 p = res->ptr + exp;
338 free(s);
339 e = res->ptr + res->len;
340 if (0 < (n = dscale - ((intptr_t)e - (intptr_t)p - 1)))
341 strpad(&res, res->len + n, '0', STR_LEFT);
342 mpf_clear(f);
343 return res;
345 mpf_clear(f);
346 return NULL;
348 #endif