fraction/fraction/
postgres_support.rs

1extern crate byteorder;
2extern crate bytes;
3
4use self::byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
5use self::bytes::{BufMut, BytesMut};
6use postgres_types::{FromSql, IsNull, ToSql, Type};
7
8use crate::{GenericFraction, Sign, Zero};
9use division::{divide_integral, divide_rem};
10use generic::GenericInteger;
11
12use std::error::Error;
13use std::fmt;
14use std::mem;
15
16const PG_POS: u16 = 0x0000;
17const PG_NEG: u16 = 0x4000;
18const PG_NAN: u16 = 0xC000;
19const PG_NBASE_U: u16 = 10000;
20const PG_NBASE_I: i16 = 10000;
21pub const PG_MAX_PRECISION: usize = 16383;
22
23#[inline]
24pub fn read_i16(mut buf: &[u8]) -> Result<i16, Box<dyn Error + Sync + Send>> {
25    match buf.read_i16::<BigEndian>() {
26        Ok(n) => Ok(n),
27        Err(e) => Err(e.into()),
28    }
29}
30
31#[inline]
32pub fn read_u16(mut buf: &[u8]) -> Result<u16, Box<dyn Error + Sync + Send>> {
33    match buf.read_u16::<BigEndian>() {
34        Ok(n) => Ok(n),
35        Err(e) => Err(e.into()),
36    }
37}
38
39#[inline]
40pub fn write_i16(mut buf: &mut [u8], value: i16) -> Result<(), Box<dyn Error + Sync + Send>> {
41    match buf.write_i16::<BigEndian>(value) {
42        Ok(()) => Ok(()),
43        Err(e) => Err(e.into()),
44    }
45}
46
47#[inline]
48pub fn write_u16(mut buf: &mut [u8], value: u16) -> Result<(), Box<dyn Error + Sync + Send>> {
49    match buf.write_u16::<BigEndian>(value) {
50        Ok(()) => Ok(()),
51        Err(e) => Err(e.into()),
52    }
53}
54
55impl<'a, T> FromSql<'a> for GenericFraction<T>
56where
57    T: Clone + GenericInteger + From<u16>,
58{
59    fn from_sql(_ty: &Type, raw: &'a [u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
60        if raw.len() < 8 {
61            return Err("unexpected data package from the database".into());
62        }
63
64        let sign: u16 = read_u16(&raw[4..6])?;
65        let sign = match sign {
66            PG_NEG => Sign::Minus,
67            PG_POS => Sign::Plus,
68            PG_NAN => return Ok(Self::nan()),
69            _ => return Err("unexpected sign byte in the data package".into()),
70        };
71
72        let ndigits: i16 = read_i16(&raw[0..2])?;
73        if ndigits <= 0 {
74            return Ok(match sign {
75                Sign::Minus => Self::neg_zero(),
76                Sign::Plus => Self::zero(),
77            });
78        }
79
80        // safe to transmute as it is > 0
81        let ndigits: usize = unsafe { mem::transmute::<i16, u16>(ndigits) }.into();
82        if (raw.len() - 8) / 2 < ndigits {
83            return Err("data package declares more digits than received".into());
84        }
85
86        let weight: i16 = read_i16(&raw[2..4])?;
87        let uweight: usize = if weight <= 0 {
88            0
89        } else {
90            // safe to transmute as it is > 0
91            unsafe { mem::transmute::<i16, u16>(weight) }.into()
92        };
93
94        let mut num: T = 0u16.into();
95        let mut den: T = 1u16.into();
96        let mut exp: T = 1u16.into();
97
98        let nbase: T = PG_NBASE_U.into();
99
100        let overflow_message = "integer overflow during unpacking the database value (try to use bigger integer as the base for Fraction, or you may even try BigFraction to use heap memory)";
101
102        if weight < 0 {
103            for _ in weight..0 {
104                den = match den.checked_mul(&nbase) {
105                    Some(n) => n,
106                    None => return Err(overflow_message.into()),
107                };
108            }
109        } else {
110            for _ in 0..weight {
111                exp = match exp.checked_mul(&nbase) {
112                    Some(n) => n,
113                    None => return Err(overflow_message.into()),
114                };
115            }
116        }
117
118        for iteration in 0..ndigits {
119            // TODO: check if we could do GCD it within the loop
120            let i = 8 + iteration * 2;
121
122            let digits: i16 = read_i16(&raw[i..i + 2])?;
123            let mut digits = if digits < 0 {
124                return Err("database sent unexpected negative value".into());
125            } else {
126                unsafe { mem::transmute::<i16, u16>(digits) }
127            };
128
129            /* Digit x000 */
130            let digit: u16 = digits / 1000 * 1000;
131            digits -= digit;
132
133            let d: T = digit.into();
134            let d = match d.checked_mul(&exp) {
135                Some(n) => n,
136                None => return Err(overflow_message.into()),
137            };
138
139            num = match num.checked_add(&d) {
140                Some(n) => n,
141                None => return Err(overflow_message.into()),
142            };
143
144            /* Digit 0x00 */
145            let digit: u16 = digits / 100 * 100;
146            digits -= digit;
147
148            let d: T = digit.into();
149            let d = match d.checked_mul(&exp) {
150                Some(n) => n,
151                None => return Err(overflow_message.into()),
152            };
153            num = match num.checked_add(&d) {
154                Some(n) => n,
155                None => return Err(overflow_message.into()),
156            };
157
158            /* Digit 00x0 */
159            let digit: u16 = digits / 10 * 10;
160            digits -= digit;
161
162            let d: T = digit.into();
163            let d = match d.checked_mul(&exp) {
164                Some(n) => n,
165                None => return Err(overflow_message.into()),
166            };
167            num = match num.checked_add(&d) {
168                Some(n) => n,
169                None => return Err(overflow_message.into()),
170            };
171
172            /* Digit 000x */
173            let d: T = digits.into();
174            let d = match d.checked_mul(&exp) {
175                Some(n) => n,
176                None => return Err(overflow_message.into()),
177            };
178            num = match num.checked_add(&d) {
179                Some(n) => n,
180                None => return Err(overflow_message.into()),
181            };
182
183            /* maintain the exponential growth */
184            if iteration >= uweight {
185                // after the decimal point
186                num = match num.checked_mul(&nbase) {
187                    Some(n) => n,
188                    None => return Err(overflow_message.into()),
189                };
190
191                den = match den.checked_mul(&nbase) {
192                    Some(n) => n,
193                    None => return Err(overflow_message.into()),
194                };
195            } else if uweight > 0 {
196                // before the decimal point
197                exp = match exp.checked_div(&nbase) {
198                    Some(n) => n,
199                    None => return Err(overflow_message.into()),
200                };
201            }
202        }
203
204        Ok(match sign {
205            Sign::Plus => Self::new(num, den),
206            Sign::Minus => Self::new_neg(num, den),
207        })
208    }
209
210    accepts!(NUMERIC);
211}
212
213impl<T> ToSql for GenericFraction<T>
214where
215    T: Clone + GenericInteger + From<u8> + fmt::Debug,
216{
217    fn to_sql(
218        &self,
219        ty: &Type,
220        buf: &mut BytesMut,
221    ) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
222        fraction_to_sql_buf(self, ty, buf, PG_MAX_PRECISION)
223    }
224
225    accepts!(NUMERIC);
226
227    to_sql_checked!();
228}
229
230pub fn fraction_to_sql_buf<T>(
231    source: &GenericFraction<T>,
232    _ty: &Type,
233    buf: &mut BytesMut,
234    precision: usize,
235) -> Result<IsNull, Box<dyn Error + Sync + Send>>
236where
237    T: Clone + GenericInteger + From<u8>,
238{
239    let precision = if precision <= PG_MAX_PRECISION {
240        precision
241    } else {
242        PG_MAX_PRECISION
243    };
244
245    let buffer_offset: usize = buf.len();
246    buf.put_u64(0); // fill in the first 8 bytes
247
248    if source.is_zero() {
249        return Ok(IsNull::No);
250    }
251
252    if source.is_nan() {
253        write_u16(&mut buf[buffer_offset + 4..buffer_offset + 6], PG_NAN)?;
254        return Ok(IsNull::No);
255    }
256
257    let numer: T = if let Some(n) = source.numer() {
258        n.clone()
259    } else {
260        unreachable!();
261    };
262    let denom: T = if let Some(d) = source.denom() {
263        d.clone()
264    } else {
265        unreachable!();
266    };
267
268    let mut ndigits: i16 = 0;
269    let mut weight: i16 = -1;
270    let mut scale: i16 = 0;
271    let mut uscale: usize = 0;
272
273    let mut ndigit: i16 = 0;
274    let mut nptr: u32 = 0;
275
276    let mut padding = true;
277    let mut rpad = 0;
278
279    let div_state = divide_integral(numer, denom, |digit: u8| {
280        if padding && digit == 0 {
281            return Ok(true);
282        } else {
283            padding = false;
284        }
285
286        let digit: i16 = digit.into();
287
288        ndigit *= 10;
289        ndigit += digit;
290
291        nptr += 1;
292
293        if nptr > 3 {
294            nptr = 0;
295            weight += 1;
296
297            if ndigit == 0 {
298                rpad += 1;
299            } else {
300                rpad = 0;
301            }
302
303            ndigits += 1;
304            buf.put_i16(ndigit);
305            ndigit = 0;
306        }
307
308        Ok(true)
309    })?;
310
311    if nptr != 0 {
312        let shift = 4 - nptr;
313
314        ndigits += 1;
315        weight += 1;
316        nptr = 0;
317
318        let digits = (buf.len() - buffer_offset - 8) / 2;
319        let mut rem_: i16 = 0;
320
321        for i in 0..digits {
322            let pos = buffer_offset + 8 + i * 2;
323            let mut digit = read_i16(&buf[pos..pos + 2])?;
324
325            let mut tmp_rem: i16 = (digit - (digit / 10 * 10)) * 1000;
326            digit /= 10;
327
328            if shift > 1 {
329                tmp_rem /= 10;
330                tmp_rem += (digit - (digit / 10 * 10)) * 1000;
331                digit /= 10;
332
333                if shift > 2 {
334                    tmp_rem /= 10;
335                    tmp_rem += (digit - (digit / 10 * 10)) * 1000;
336                    digit /= 10;
337                }
338            }
339
340            digit += rem_;
341            rem_ = tmp_rem;
342
343            write_i16(&mut buf[pos..pos + 2], digit)?;
344        }
345
346        ndigit += rem_;
347        if ndigit == 0 {
348            rpad += 1;
349        } else {
350            rpad = 0;
351        }
352
353        buf.put_i16(ndigit);
354        ndigit = 0;
355    }
356
357    if div_state.remainder.is_zero() && rpad > 0 {
358        ndigits -= rpad;
359        buf.truncate(buffer_offset + 8 + (ndigits as usize) * 2);
360    }
361
362    if !div_state.remainder.is_zero() {
363        padding = weight < 0; // true;
364        divide_rem(
365            div_state.remainder,
366            div_state.divisor,
367            |state, digit: u8| {
368                let digit: i16 = digit.into();
369
370                if digit != 0 {
371                    if padding && weight > 0 {
372                        ndigits += weight;
373                        for _ in 0..weight {
374                            buf.put_i16(ndigit);
375                        }
376                    }
377                    padding = false;
378                }
379
380                nptr += 1;
381
382                ndigit += digit * (PG_NBASE_I / 10i16.pow(nptr));
383
384                scale += 1;
385                uscale += 1;
386
387                if nptr > 3 {
388                    if padding && weight < 0 {
389                        weight -= 1;
390                    } else {
391                        ndigits += 1;
392                        buf.put_i16(ndigit);
393                    }
394
395                    nptr = 0;
396                    ndigit = 0;
397                }
398
399                Ok(if uscale < precision {
400                    Ok(state)
401                } else {
402                    Err(state)
403                })
404            },
405        )?;
406
407        if nptr != 0 && !padding {
408            ndigits += 1;
409            buf.put_i16(ndigit);
410        }
411    }
412
413    write_i16(&mut buf[buffer_offset..buffer_offset + 2], ndigits)?;
414    write_i16(&mut buf[buffer_offset + 2..buffer_offset + 4], weight)?;
415    write_u16(
416        &mut buf[buffer_offset + 4..buffer_offset + 6],
417        match source.sign() {
418            Some(Sign::Minus) => PG_NEG,
419            _ => PG_POS,
420        },
421    )?;
422
423    write_i16(&mut buf[buffer_offset + 6..buffer_offset + 8], scale)?;
424
425    Ok(IsNull::No)
426}
427
428#[cfg(test)]
429mod tests {
430    use super::*;
431
432    type Fraction = GenericFraction<u128>;
433    const NUMERIC_OID: u32 = 1700;
434
435    fn get_tests() -> Vec<(Fraction, &'static [u8])> {
436        vec![
437            (
438                Fraction::new_raw_signed(Sign::Minus, 12345678901234u128, 1u128),
439                &[0, 4, 0, 3, 64, 0, 0, 0, 0, 12, 13, 128, 30, 210, 4, 210],
440            ),
441            (
442                Fraction::new_raw_signed(Sign::Minus, 12345678u128, 1u128),
443                &[0, 2, 0, 1, 64, 0, 0, 0, 4, 210, 22, 46],
444            ),
445            (
446                Fraction::new_raw_signed(Sign::Minus, 10000000000000000001u128, 10000000000u128),
447                &[
448                    0, 6, 0, 2, 64, 0, 0, 10, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100,
449                ],
450            ),
451            (
452                Fraction::new_raw_signed(Sign::Minus, 1000000000u128, 1u128),
453                &[0, 1, 0, 2, 64, 0, 0, 0, 0, 10],
454            ),
455            (
456                Fraction::new_raw_signed(Sign::Minus, 1234u128, 1u128),
457                &[0, 1, 0, 0, 64, 0, 0, 0, 4, 210],
458            ),
459            (
460                Fraction::new_raw_signed(Sign::Minus, 256u128, 1u128),
461                &[0, 1, 0, 0, 64, 0, 0, 0, 1, 0],
462            ),
463            (
464                Fraction::new_raw_signed(Sign::Minus, 42u128, 1u128),
465                &[0, 1, 0, 0, 64, 0, 0, 0, 0, 42],
466            ),
467            (
468                Fraction::new_raw_signed(Sign::Minus, 1u128, 1u128),
469                &[0, 1, 0, 0, 64, 0, 0, 0, 0, 1],
470            ),
471            (
472                Fraction::new_raw_signed(Sign::Minus, 1u128, 2u128),
473                &[0, 1, 255, 255, 64, 0, 0, 1, 19, 136],
474            ),
475            (
476                Fraction::new_raw_signed(Sign::Minus, 1u128, 10u128),
477                &[0, 1, 255, 255, 64, 0, 0, 1, 3, 232],
478            ),
479            (
480                Fraction::new_raw_signed(Sign::Minus, 66u128, 100u128),
481                &[0, 1, 255, 255, 64, 0, 0, 2, 25, 200],
482            ),
483            (
484                Fraction::new_raw_signed(Sign::Minus, 6172839450617u128, 50000000000000u128),
485                &[0, 4, 255, 255, 64, 0, 0, 14, 4, 210, 22, 46, 35, 52, 13, 72],
486            ),
487            (
488                Fraction::new_raw_signed(Sign::Minus, 1u128, 100u128),
489                &[0, 1, 255, 255, 64, 0, 0, 2, 0, 100],
490            ),
491            (
492                Fraction::new_raw_signed(Sign::Minus, 601u128, 2500u128),
493                &[0, 1, 255, 255, 64, 0, 0, 4, 9, 100],
494            ),
495            (
496                Fraction::new_raw_signed(Sign::Minus, 1u128, 1000000000u128),
497                &[0, 1, 255, 253, 64, 0, 0, 9, 3, 232],
498            ),
499            (
500                Fraction::new_raw_signed(
501                    Sign::Minus,
502                    617283945061706172839450617u128,
503                    50000000000000u128,
504                ),
505                &[
506                    0, 8, 0, 3, 64, 0, 0, 14, 0, 12, 13, 128, 30, 210, 4, 210, 4, 210, 22, 46, 35,
507                    52, 13, 72,
508                ],
509            ),
510            (Fraction::zero(), &[0, 0, 0, 0, 0, 0, 0, 0]),
511            (Fraction::nan(), &[0, 0, 0, 0, 192, 0, 0, 0]),
512            (
513                Fraction::new_raw(617283945061706172839450617u128, 50000000000000u128),
514                &[
515                    0, 8, 0, 3, 0, 0, 0, 14, 0, 12, 13, 128, 30, 210, 4, 210, 4, 210, 22, 46, 35,
516                    52, 13, 72,
517                ],
518            ),
519            (
520                Fraction::new_raw(1u128, 1000000000u128),
521                &[0, 1, 255, 253, 0, 0, 0, 9, 3, 232],
522            ),
523            (
524                Fraction::new_raw(601u128, 2500u128),
525                &[0, 1, 255, 255, 0, 0, 0, 4, 9, 100],
526            ),
527            (
528                Fraction::new_raw(1u128, 100u128),
529                &[0, 1, 255, 255, 0, 0, 0, 2, 0, 100],
530            ),
531            (
532                Fraction::new_raw(66u128, 100u128),
533                &[0, 1, 255, 255, 0, 0, 0, 2, 25, 200],
534            ),
535            (
536                Fraction::new_raw(10000000000000000000001u128, 10000000000000000u128),
537                &[
538                    0, 6, 0, 1, 0, 0, 0, 16, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
539                ],
540            ),
541            (
542                Fraction::new_raw(6172839450617u128, 50000000000000u128),
543                &[0, 4, 255, 255, 0, 0, 0, 14, 4, 210, 22, 46, 35, 52, 13, 72],
544            ),
545            (
546                Fraction::new_raw(1u128, 10u128),
547                &[0, 1, 255, 255, 0, 0, 0, 1, 3, 232],
548            ),
549            (
550                Fraction::new_raw(1u128, 2u128),
551                &[0, 1, 255, 255, 0, 0, 0, 1, 19, 136],
552            ),
553            (
554                Fraction::new_raw(1u128, 1u128),
555                &[0, 1, 0, 0, 0, 0, 0, 0, 0, 1],
556            ),
557            (
558                Fraction::new_raw(42u128, 1u128),
559                &[0, 1, 0, 0, 0, 0, 0, 0, 0, 42],
560            ),
561            (
562                Fraction::new_raw(256u128, 1u128),
563                &[0, 1, 0, 0, 0, 0, 0, 0, 1, 0],
564            ),
565            (
566                Fraction::new_raw(1234u128, 1u128),
567                &[0, 1, 0, 0, 0, 0, 0, 0, 4, 210],
568            ),
569            (
570                Fraction::new_raw(1000000000u128, 1u128),
571                &[0, 1, 0, 2, 0, 0, 0, 0, 0, 10],
572            ),
573            (
574                Fraction::new_raw(10000000000000000001u128, 10000000000u128),
575                &[
576                    0, 6, 0, 2, 0, 0, 0, 10, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100,
577                ],
578            ),
579            (
580                Fraction::new_raw(12345678u128, 1u128),
581                &[0, 2, 0, 1, 0, 0, 0, 0, 4, 210, 22, 46],
582            ),
583            (
584                Fraction::new_raw(12345678901234u128, 1u128),
585                &[0, 4, 0, 3, 0, 0, 0, 0, 0, 12, 13, 128, 30, 210, 4, 210],
586            ),
587            (
588                Fraction::new_raw(33333333333333333333u128, 100000000000000000000u128),
589                &[
590                    0, 5, 255, 255, 0, 0, 0, 20, 13, 5, 13, 5, 13, 5, 13, 5, 13, 5,
591                ],
592            ),
593        ]
594    }
595
596    #[test]
597    fn test_from_sql() {
598        let t = Type::from_oid(NUMERIC_OID).unwrap();
599        for ref test in &get_tests() {
600            assert_eq!(
601                test.0,
602                <Fraction as FromSql>::from_sql(&t, test.1).ok().unwrap()
603            )
604        }
605    }
606
607    #[test]
608    fn test_to_sql() {
609        let t = Type::from_oid(NUMERIC_OID).unwrap();
610        let mut buf = BytesMut::with_capacity(1024);
611
612        for ref test in &get_tests() {
613            buf.clear();
614            let res = <Fraction as ToSql>::to_sql(&test.0, &t, &mut buf)
615                .ok()
616                .unwrap();
617
618            match res {
619                IsNull::Yes => assert!(false),
620                IsNull::No => assert!(true),
621            };
622
623            assert_eq!(&buf, &test.1);
624        }
625    }
626}