ion_c_sys/
lib.rs

1// Copyright Amazon.com, Inc. or its affiliates.
2
3//! Provides basic bindings for [Ion C](https://github.com/amzn/ion-c)
4//!
5//! These bindings are created with `bindgen` and are considerably low-level.
6//!
7//! ## Examples
8//! Using `ion-c-sys` directly can be a pretty verbose affair, and requires checking the
9//! error code for most calls.  This crate provides the [`result`](result/index.html)
10//! module to make it easier to integrate with `std::result::Result` with respect
11//! to the `iERR` that Ion C functions generally return.  Specifically, any low-level
12//! IonC function that returns `iERR` should be called with the [`ionc!`][ionc-call] macro
13//! to facilitate `Result<(), IonCError>` conversion.
14//!
15//! This library provides smart pointers over the low-level reader/writer pointers, and should
16//! generally be used, especially with `Result` handling code.  These types provide some facade
17//! over Ion C, but only for the most generally used APIs. See:
18//!
19//! * [`IonCReader`][reader-trait]
20//! * [`IonCWriter`][writer-trait]
21//!
22//! [ionc-call]: macro.ionc.html
23//! [reader-trait]: reader/trait.IonCReader.html
24//! [writer-trait]: writer/trait.IonCWriter.html
25//!
26//! ### Ion Reader
27//! Here is an end-to-end example of reading some Ion data.
28//!
29//! ```
30//! # use std::ptr;
31//! # use std::slice;
32//! # use std::str;
33//! # use std::convert::TryFrom;
34//! # use ion_c_sys::*;
35//! # use ion_c_sys::reader::*;
36//! # use ion_c_sys::result::*;
37//! # fn main() -> IonCResult<()> {
38//! let mut reader = IonCReaderHandle::try_from("{a:2}")?;
39//!
40//! // step to the struct
41//! assert_eq!(ION_TYPE_STRUCT, reader.next()?);
42//! // step into the struct
43//! reader.step_in()?;
44//! // step to the field
45//! assert_eq!(ION_TYPE_INT, reader.next()?);
46//! // retrieve the field name
47//! assert_eq!("a", reader.get_field_name()?.as_str());
48//! // read the integer value
49//! assert_eq!(2, reader.read_i64()?);
50//! // step to the end of the struct
51//! assert_eq!(ION_TYPE_EOF, reader.next()?);
52//! // step out of the struct
53//! reader.step_out()?;
54//! // step to the end of the stream
55//! assert_eq!(ION_TYPE_EOF, reader.next()?);
56//!
57//! # Ok(())
58//! # }
59//! ```
60//!
61//! ### Ion Writer
62//! Here is an end-to-end example of writing some Ion data.
63//!
64//! ```
65//! # use std::ptr;
66//! # use std::convert::TryInto;
67//! # use ion_c_sys::*;
68//! # use ion_c_sys::result::*;
69//! # use ion_c_sys::writer::*;
70//! # fn main() -> IonCResult<()> {
71//! // output buffer
72//! let mut buf: Vec<u8> = vec![0; 128];
73//! let len = {
74//!     let mut writer = IonCWriterHandle::new_buf_mode(buf.as_mut(), WriterMode::Binary)?;
75//!     // start a list
76//!     writer.start_container(ION_TYPE_LIST)?;
77//!     // write some integers
78//!     for n in 0..4 {
79//!         writer.write_i64(n * 2)?;
80//!     }
81//!     // end the list
82//!     writer.finish_container()?;
83//!     // start a struct
84//!     writer.start_container(ION_TYPE_STRUCT)?;
85//!     {
86//!         // write a string
87//!         writer.field("name").annotations(&["version"]).write_string("💩")?;
88//!     }
89//!     // end the struct
90//!     writer.finish_container()?;
91//!     // finish writing
92//!     writer.finish()?
93//! };
94//!
95//! // make sure the bytes match what we expect
96//! let expected: &[u8] = &[
97//!     0xE0, 0x01, 0x00, 0xEA,         // IVM
98//!     0xB7,                           // LIST size 7
99//!     0x20,                           // INT 0
100//!     0x21, 0x02,                     // INT 2
101//!     0x21, 0x04,                     // INT 4
102//!     0x21, 0x06,                     // INT 6
103//!     0xD9,                           // STRUCT size 8
104//!     0x84,                           // field "name" (sid 4)
105//!     0xE7, 0x81, 0x85,               // annotation "version" (sid 5)
106//!     0x84, 0xF0, 0x9F, 0x92, 0xA9,   // STRING 💩
107//! ];
108//! assert_eq!(expected.len(), len);
109//! assert_eq!(expected, &buf[0..len]);
110//!
111//! # Ok(())
112//! # }
113//! ```
114
115#![allow(non_upper_case_globals)]
116#![allow(non_camel_case_types)]
117#![allow(non_snake_case)]
118
119pub mod decimal;
120pub mod int;
121pub mod reader;
122pub mod result;
123pub mod string;
124pub mod timestamp;
125pub mod writer;
126
127include!(concat!(env!("OUT_DIR"), "/ionc_bindings.rs"));
128
129use crate::decimal::IonDecimalPtr;
130use crate::int::IonIntPtr;
131use crate::result::*;
132use crate::timestamp::Mantissa::*;
133use crate::timestamp::TSOffsetKind::*;
134use crate::timestamp::TSPrecision::*;
135use crate::timestamp::{IonDateTime, Mantissa, TSOffsetKind, TS_MAX_MANTISSA_DIGITS};
136
137use std::cmp::min;
138use std::convert::TryInto;
139use std::marker::PhantomData;
140use std::ptr;
141use std::str::Utf8Error;
142use std::{slice, str};
143
144use bigdecimal::{BigDecimal, ToPrimitive, Zero};
145use chrono::offset::FixedOffset;
146use chrono::{Datelike, LocalResult, TimeZone, Timelike};
147use num_bigint::{BigInt, Sign};
148use paste::paste;
149
150#[cfg(test)]
151use rstest_reuse;
152
153impl ION_INT {
154    /// Constructs a `BigInt` from this `ION_INT`.
155    ///
156    /// Note that since `BigInt` does not have a ***view*** into its digits,
157    /// this method will make an intermediate copy as the big-endian encoded
158    /// byte vector that will then be stored into this `ION_INT`
159    pub fn try_assign_bigint(&mut self, src: &BigInt) -> IonCResult<()> {
160        let (sign, mut raw_mag) = src.to_bytes_be();
161        let is_neg = match sign {
162            Sign::Minus => 1,
163            _ => 0,
164        };
165
166        ionc!(ion_int_from_abs_bytes(
167            &mut *self,
168            raw_mag.as_mut_ptr(),
169            raw_mag.len().try_into()?,
170            is_neg
171        ))?;
172
173        Ok(())
174    }
175
176    /// Constructs a `BigInt` from this `ION_INT`.
177    pub fn try_to_bigint(&self) -> IonCResult<BigInt> {
178        if self._digits.is_null() {
179            return Err(IonCError::from(ion_error_code_IERR_NULL_VALUE));
180        }
181        if self._len < 0 {
182            return Err(IonCError::from(ion_error_code_IERR_INVALID_ARG));
183        }
184        if self._signum < -1 || self._signum > 1 {
185            return Err(IonCError::from(ion_error_code_IERR_INVALID_ARG));
186        }
187        let src_digits = unsafe { slice::from_raw_parts(self._digits, self._len.try_into()?) };
188
189        // figure out how many BigInt digits we need keeping in mind that
190        // ION_INT is base 2**31, and BigInt is base 2**32.
191        const ION_INT_BITS: u64 = 31;
192        const BIGINT_BITS: u64 = 32;
193        const ION_INT_DIGIT_MASK: u64 = 0x7FFF_FFFF;
194        let tgt_len = (((self._len as u64) * ION_INT_BITS) / BIGINT_BITS) + 1;
195        let mut digits = vec![0u32; tgt_len.try_into()?];
196
197        // total bits written
198        let mut bits_written = 0u64;
199        // note that we go from back to front for ION_INT as it is big-endian
200        // but BigInt is little-endian
201        for src_digit in src_digits.iter().rev() {
202            // get the source digit to deposit into the target digit(s)
203            let src_digit = (*src_digit as u64) & ION_INT_DIGIT_MASK;
204            // which target digit are we working on
205            let tgt_idx = (bits_written >> 5) as usize;
206            // how many bits are used in the current target digit
207            let filled_bits = bits_written & 0x1F;
208            // how many bits we can fit in the current target digit
209            let avail_bits = BIGINT_BITS - filled_bits;
210            // how many source bits have to go into the next target digit
211            let rem_bits = ION_INT_BITS - min(ION_INT_BITS, avail_bits);
212
213            // push the low order bits of the source into the available high order bits of the target
214            let old_tgt_digit = digits[tgt_idx];
215            let high_bit_mask = (src_digit << filled_bits) as u32;
216            let new_tgt_digit = old_tgt_digit | high_bit_mask;
217            digits[tgt_idx] = new_tgt_digit;
218
219            if tgt_idx + 1 < digits.len() && rem_bits > 0 {
220                // push the remaining high order bits into the low order bits of the next target digit
221                let next_idx = tgt_idx + 1;
222                let shift_bits = ION_INT_BITS - rem_bits;
223                let next_tgt_digit = (src_digit >> shift_bits) as u32;
224                digits[next_idx] = next_tgt_digit;
225            }
226
227            bits_written += ION_INT_BITS as u64;
228        }
229
230        const SIGN_TABLE: &[Sign] = &[Sign::Minus, Sign::NoSign, Sign::Plus];
231        Ok(BigInt::new(SIGN_TABLE[(self._signum + 1) as usize], digits))
232    }
233}
234
235#[cfg(test)]
236mod test_bigint {
237    use crate::int::*;
238    use crate::result::*;
239    use crate::*;
240
241    use rstest::rstest;
242    use rstest_reuse::{self, *};
243
244    use num_bigint::BigInt;
245    use num_bigint::Sign::{self, *};
246
247    // TODO consider some kind of fuzz/property testing for this
248
249    #[template]
250    #[rstest(
251        lit,
252        sign,
253        case::zero("0", NoSign),
254        case::pos_31_bit("1576217826", Plus),
255        case::neg_31_bit("-1135682218", Minus),
256        case::pos_62_bit("4044881356853627201", Plus),
257        case::neg_62_bit("-3912230224800585615", Minus),
258        case::pos_80_bit("739079489563988370954567", Plus),
259        case::neg_80_bit("-1086195751445330490038795", Minus),
260        case::pos_256_bit(
261            "137867910096739512996847672171101012368076859213341045932878406344693462874820",
262            Plus,
263        ),
264        case::neg_256_bit(
265            "-172272298565065214306566076919200322665607032158922187439565911507697602517448",
266            Minus,
267        ),
268        case::pos_280_bit(
269            "1757357796823956205198798709416201514711937158830789249081025568737706527211427788829",
270            Plus,
271        ),
272        case::neg_280_bit(
273            "-1075268761612498909802747877455511969232059561308078408290306546278351574885791689247",
274            Minus,
275        )
276    )]
277    fn bigint(lit: &str, sign: Sign) {}
278
279    #[apply(bigint)]
280    fn try_assign_bigint(lit: &str, sign: Sign) -> IonCResult<()> {
281        let big_val = BigInt::parse_bytes(lit.as_bytes(), 10).unwrap();
282        let mut ion_val = IonIntPtr::try_from_bigint(&big_val)?;
283
284        let mut buf = vec![0u8; 512];
285        let mut len = 0;
286        ionc!(ion_int_to_char(
287            ion_val.as_mut_ptr(),
288            buf.as_mut_ptr(),
289            buf.len().try_into()?,
290            &mut len
291        ))?;
292
293        let expected_signum = match sign {
294            Minus => -1,
295            NoSign => 0,
296            Plus => 1,
297        };
298        let mut actual_signum = 0;
299        ionc!(ion_int_signum(ion_val.as_mut_ptr(), &mut actual_signum))?;
300        assert_eq!(expected_signum, actual_signum);
301        assert_eq!(lit.as_bytes(), &buf[0..len.try_into()?]);
302
303        Ok(())
304    }
305
306    #[apply(bigint)]
307    fn try_to_bigint(lit: &str, sign: Sign) -> IonCResult<()> {
308        let mut ion_val = IonIntPtr::try_new()?;
309
310        let mut ion_str = ION_STRING::try_from_str(lit)?;
311        ionc!(ion_int_from_string(ion_val.as_mut_ptr(), &mut ion_str))?;
312
313        let big_val = ion_val.try_to_bigint()?;
314        assert_eq!(sign, big_val.sign());
315        assert_eq!(lit, big_val.to_string().as_str());
316
317        Ok(())
318    }
319}
320
321/// Creates an operating context for decNum with maximum ranges primarily for conversions
322/// between `ION_DECIMAL` and `BigDecimal`.
323///
324/// This range is still below the capabilities of `BigDecimal`
325#[inline]
326fn make_context() -> decContext {
327    let mut ctx = decContext::default();
328    unsafe { decContextDefault(&mut ctx, DEC_INIT_DECQUAD as i32) };
329    ctx.digits = DEC_MAX_DIGITS as i32;
330    ctx.emax = DEC_MAX_EMAX as i32;
331    ctx.emin = DEC_MIN_EMIN as i32;
332    // make sure we don't pad decQuads
333    ctx.clamp = 0;
334
335    ctx
336}
337
338// TODO amzn/ion-rust#79 - we need to fix decNumber support.
339const DEC_NUMBER_ERROR_MSG: &str = "DecNumber Not Supported";
340
341impl ION_DECIMAL {
342    /// Assigns a `BigDecimal` into this `ION_DECIMAL`.
343    pub fn try_assign_bigdecimal(&mut self, value: &BigDecimal) -> IonCResult<()> {
344        let digits = value.digits();
345
346        // FIXME amzn/ion-rust#80 - this breaks encapsulation
347        ionc!(ion_decimal_free(self))?;
348        if digits > (DECQUAD_Pmax as u64) {
349            self.type_ = ION_DECIMAL_TYPE_ION_DECIMAL_TYPE_NUMBER;
350            // need to allocate the number field
351            ionc!(_ion_decimal_number_alloc(
352                ptr::null_mut(),
353                digits.try_into()?,
354                &mut self.value.num_value,
355            ))?;
356        } else {
357            self.type_ = ION_DECIMAL_TYPE_ION_DECIMAL_TYPE_QUAD;
358        }
359        ionc!(ion_decimal_zero(self))?;
360
361        let mut ctx = make_context();
362        let (coefficient, scale) = value.as_bigint_and_exponent();
363
364        // set the coefficient
365        let mut ion_coefficient = IonIntPtr::try_from_bigint(&coefficient)?;
366        ionc!(ion_decimal_from_ion_int(
367            self,
368            &mut ctx,
369            ion_coefficient.as_mut_ptr()
370        ))?;
371
372        // scale the value
373        let mut dec_scale = IonDecimalPtr::try_from_i32((-scale).try_into()?)?;
374        ionc!(ion_decimal_scaleb(
375            self,
376            self,
377            dec_scale.as_mut_ptr(),
378            &mut ctx
379        ))?;
380
381        match unsafe { decContextGetStatus(&mut ctx) } {
382            0 => Ok(()),
383            // FIXME amzn/ion-rust#79 - we need to fix decNumber support.
384            DEC_Invalid_context => Err(IonCError::with_additional(
385                ion_error_code_IERR_INVALID_STATE,
386                DEC_NUMBER_ERROR_MSG,
387            )),
388            _ => Err(IonCError::from(ion_error_code_IERR_INTERNAL_ERROR)),
389        }
390    }
391
392    /// Converts this `ION_DECIMAL` to a `BigDecimal`.
393    ///
394    /// Special decimal values such as NaN and infinity are not supported for conversion.
395    ///
396    /// This implementation borrows mutably, to avoid a copy of the underlying
397    /// decimal implementation, but does not change the value.
398    ///
399    /// Specifically, this code scales from/to the exponent the value to extract the coefficient.
400    pub fn try_to_bigdecimal(&mut self) -> IonCResult<BigDecimal> {
401        // special values are not supported
402        let special =
403            unsafe { ion_decimal_is_nan(self) } | unsafe { ion_decimal_is_infinite(self) };
404        if special != 0 {
405            return Err(IonCError::from(ion_error_code_IERR_INVALID_ARG));
406        }
407
408        // get some information about the decimal
409        let exponent = unsafe { ion_decimal_get_exponent(self) };
410        let mut ctx = make_context();
411
412        // scale the value to an integer
413        let mut scale_amount = IonDecimalPtr::try_from_i32(-exponent)?;
414        ionc!(ion_decimal_scaleb(
415            self,
416            self,
417            scale_amount.as_mut_ptr(),
418            &mut ctx
419        ))?;
420        let mut ion_coefficient = IonIntPtr::try_new()?;
421        ionc!(ion_decimal_to_ion_int(
422            self,
423            &mut ctx,
424            ion_coefficient.as_mut_ptr()
425        ))?;
426
427        // scale back the value
428        ionc!(ion_decimal_from_int32(scale_amount.as_mut_ptr(), exponent))?;
429        ionc!(ion_decimal_scaleb(
430            self,
431            self,
432            scale_amount.as_mut_ptr(),
433            &mut ctx
434        ))?;
435
436        // make the decimal -- note scale is negative exponent
437        let coefficient = ion_coefficient.try_to_bigint()?;
438        Ok(BigDecimal::new(coefficient, -exponent as i64))
439    }
440}
441
442#[cfg(test)]
443mod test_bigdecimal {
444    // because of test table re-use we sometimes end up with unused variables
445    #![allow(unused_variables)]
446
447    use crate::decimal::*;
448    use crate::result::*;
449    use crate::*;
450
451    use rstest::rstest;
452    use rstest_reuse::{self, *};
453
454    use std::ffi::CString;
455
456    use bigdecimal::BigDecimal;
457    use num_bigint::BigInt;
458    use std::os::raw::c_char;
459
460    // TODO consider some kind of fuzz/property testing for this
461
462    #[template]
463    #[rstest(
464        d_lit, c_lit, exponent,
465        case::zero("0E0", "0", 0),
466        case::zero_p1_en8("0E-8", "0", -8),
467        case::n8_en9(
468            "-0.060231219",
469            "-60231219",
470            -9
471        ),
472        case::p8_en9(
473            "0.060231219",
474            "60231219",
475            -9
476        ),
477        case::n8_e7(
478            "-2.7851880E+14",
479            "-27851880",
480            7
481        ),
482        case::p8_e7(
483            "2.7851880E+14",
484            "27851880",
485            7
486        ),
487        case::n10_en32(
488            "-8.960115983E-23",
489            "-8960115983",
490            -32
491        ),
492        case::p10_en32(
493            "8.960115983E-23",
494            "8960115983",
495            -32
496        ),
497        case::n10_e33(
498            "-9.020634788E+42",
499            "-9020634788",
500            33
501        ),
502        case::p10_e33(
503            "9.020634788E+42",
504            "9020634788",
505            33
506        ),
507        case::p28_en200(
508            "6.262354479103128947759990609E-173",
509            "6262354479103128947759990609",
510            -200
511        ),
512        case::p28_e256(
513            "9.486968202420944975464220485E+283",
514            "9486968202420944975464220485",
515            256
516        ),
517        case::p30_en80(
518            "1.95671174876514167707949046494E-51",
519            "195671174876514167707949046494",
520            -80
521        ),
522        case::p30_e85(
523            "6.50276908237082165030429776240E+114",
524            "650276908237082165030429776240",
525            85
526        ),
527        case::p32_en2500(
528            "4.1111441587902074230255158471962E-2469",
529            "41111441587902074230255158471962",
530            -2500
531        ),
532        case::p32_e2000(
533            "2.9557665423302931520009385209142E+2031",
534            "29557665423302931520009385209142",
535            2000
536        ),
537        case::p34_en6100(
538            "7.550261799183309871071383313529625E-6067",
539            "7550261799183309871071383313529625",
540            -6100
541        ),
542        case::p34_e6110(
543            "6.385180511995820599706505142429397E+6143",
544            "6385180511995820599706505142429397",
545            6110
546        ),
547        case::p35_en5500(
548            "5.7516904150035820771702738217456585E-5466",
549            "57516904150035820771702738217456585",
550            -5500
551        ),
552        case::p35_e4500(
553            "7.7008733801862767698341856677462573E+4534",
554            "77008733801862767698341856677462573",
555            4500
556        ),
557        case::p37_en5000(
558            "4.623874712756984956766514373293465450E-4964",
559            "4623874712756984956766514373293465450",
560            -5000
561        ),
562        case::p37_e6000(
563            "9.970304500552494301196940956407522192E+6036",
564            "9970304500552494301196940956407522192",
565            6000
566        ),
567        case::p38_en110(
568            "5.9025723530133359201873978774331457987E-73",
569            "59025723530133359201873978774331457987",
570            -110
571        ),
572        case::p38_e120(
573            "1.9141033431585215614236049655094977149E+157",
574            "19141033431585215614236049655094977149",
575            120
576        ),
577        case::p80_en100(
578            "4.4818084368071883463971983799827359329516552715395136426901699171061657459862827E-21",
579            "44818084368071883463971983799827359329516552715395136426901699171061657459862827",
580            -100
581        ),
582        case::p80_e100(
583            "1.1050923646496935500303958597719760541602476378798913802309108901778807590265278E+279",
584            "11050923646496935500303958597719760541602476378798913802309108901778807590265278",
585            200
586        ),
587        case::p90_en7500(
588            "4.29926815238794553771012929776377470059985366468509955296419658127318674237363065734779169E-7411",
589            "429926815238794553771012929776377470059985366468509955296419658127318674237363065734779169",
590            -7500
591        ),
592        case::p95_e8900(
593            "7.2528362571947544011741381371920928164665526193613163529470013995124245887784236337589645597617E+8994",
594            "72528362571947544011741381371920928164665526193613163529470013995124245887784236337589645597617",
595            8900
596        ),
597    )]
598    fn bigdecimal(d_lit: &str, c_lit: &str, exponent: i32) {}
599
600    #[apply(bigdecimal)]
601    fn try_assign_bigdecimal(d_lit: &str, c_lit: &str, exponent: i32) -> IonCResult<()> {
602        let big_val = BigDecimal::parse_bytes(d_lit.as_bytes(), 10).unwrap();
603        match IonDecimalPtr::try_from_bigdecimal(&big_val) {
604            Ok(mut ion_val) => {
605                let actual_exponent = unsafe { ion_decimal_get_exponent(ion_val.as_mut_ptr()) };
606
607                // test the string representations--not ideal, but easier than extracting coefficient
608                let mut buf = vec![0u8; 128usize];
609                ionc!(ion_decimal_to_string(
610                    ion_val.as_mut_ptr(),
611                    buf.as_mut_ptr() as *mut c_char
612                ))?;
613                let len = unsafe { strlen(buf.as_ptr() as *const c_char) };
614                assert_eq!(
615                    d_lit.replace("E", "d"),
616                    str::from_utf8(&buf[0..len.try_into()?]).unwrap(),
617                    "Testing string serialization from ION_DECIMAL"
618                );
619                assert_eq!(exponent, actual_exponent, "Testing exponents");
620            }
621            Err(e) => match e.code {
622                ion_error_code_IERR_INVALID_STATE => match e.additional {
623                    DEC_NUMBER_ERROR_MSG => println!("Ignoring amzn/ion-rust#79 for {}", d_lit),
624                    _ => assert!(false, "Unexpected error {:?}", e),
625                },
626                _ => assert!(false, "Unexpected error: {:?}", e),
627            },
628        }
629
630        Ok(())
631    }
632
633    #[apply(bigdecimal)]
634    fn try_to_bigdecimal(d_lit: &str, c_lit: &str, exponent: i32) -> IonCResult<()> {
635        let cstring = CString::new(d_lit).unwrap();
636        let mut ctx = make_context();
637        let mut ion_val = IonDecimalPtr::try_from_existing(ION_DECIMAL::default())?;
638        ionc!(ion_decimal_from_string(
639            ion_val.as_mut_ptr(),
640            cstring.as_ptr(),
641            &mut ctx
642        ))?;
643
644        let big_val = ion_val.try_to_bigdecimal()?;
645        assert_eq!(
646            big_val,
647            ion_val.try_to_bigdecimal()?,
648            "Make sure conversion is effectively immutable"
649        );
650
651        // we test against the coefficient and exponent because the string representation
652        // is not stable between decNum and BigDecimal
653        let expected_coefficient = BigInt::parse_bytes(c_lit.as_bytes(), 10).unwrap();
654        let (actual_coefficient, scale) = big_val.as_bigint_and_exponent();
655        assert_eq!(
656            expected_coefficient, actual_coefficient,
657            "Testing coefficents"
658        );
659        assert_eq!(exponent, (-scale).try_into()?, "Testing exponents");
660
661        Ok(())
662    }
663}
664
665const SEC_IN_MINS: i32 = 60;
666const NS_IN_SEC: u32 = 1_000_000_000;
667
668impl ION_TIMESTAMP {
669    // Note that although we have `IonDateTime` as a wrapper--the logic to convert
670    // to/from `ION_TIMESTAMP` is kept here to avoid the dependency on `ION_TIMESTAMP`
671    // in `IonDateTime` and to keep it consistent to the bigint and decimal conversions.
672
673    /// Converts the given `IonDateTime` into this `ION_TIMESTAMP`.
674    pub fn try_assign_from_iondt(&mut self, ion_dt: &IonDateTime) -> IonCResult<()> {
675        let dt = ion_dt.as_datetime();
676        let prec = ion_dt.precision();
677        let offset_kind = ion_dt.offset_kind();
678
679        // clear everything out
680        self.month = 1;
681        self.day = 1;
682        self.hours = 0;
683        self.minutes = 0;
684        self.seconds = 0;
685        unsafe {
686            decQuadZero(&mut self.fraction);
687        }
688        self.tz_offset = 0;
689        self.precision = 0;
690
691        // fill in the timestamp
692        self.year = dt.year().try_into()?;
693        if *prec >= Month {
694            self.month = dt.month().try_into()?;
695            if *prec >= Day {
696                self.day = dt.day().try_into()?;
697                if *prec >= Minute {
698                    self.hours = dt.hour().try_into()?;
699                    self.minutes = dt.minute().try_into()?;
700                    if *prec >= Second {
701                        self.seconds = dt.second().try_into()?;
702                        if let Fractional(mantissa) = prec {
703                            self.try_assign_mantissa(dt, mantissa)?;
704                        }
705                    }
706                }
707            }
708        }
709
710        // set the precision
711        self.precision = match prec {
712            Year => ION_TS_YEAR,
713            Month => ION_TS_MONTH,
714            Day => ION_TS_DAY,
715            Minute => ION_TS_MIN,
716            Second => ION_TS_SEC,
717            Fractional(_) => ION_TS_FRAC,
718        } as u8;
719
720        // set the offset
721        match offset_kind {
722            KnownOffset => {
723                let offset_minutes = dt.offset().local_minus_utc() / SEC_IN_MINS;
724                ionc!(ion_timestamp_set_local_offset(self, offset_minutes))?;
725            }
726            UnknownOffset => {
727                ionc!(ion_timestamp_unset_local_offset(self))?;
728            }
729        }
730
731        Ok(())
732    }
733
734    /// Assigns the fractional component.
735    fn try_assign_mantissa<T>(&mut self, dt: &T, mantissa: &Mantissa) -> IonCResult<()>
736    where
737        T: Timelike,
738    {
739        let mut ctx = make_context();
740        match mantissa {
741            Digits(digits) => {
742                unsafe {
743                    let nanos = dt.nanosecond();
744                    decQuadFromUInt32(&mut self.fraction, nanos);
745
746                    // shift over to set the precision to the number of digits
747                    let mut shift = decQuad::default();
748                    decQuadFromInt32(
749                        &mut shift,
750                        (*digits as i32) - (TS_MAX_MANTISSA_DIGITS as i32),
751                    );
752                    decQuadShift(&mut self.fraction, &self.fraction, &shift, &mut ctx);
753
754                    // scale the value to be a fraction
755                    let mut scale = decQuad::default();
756                    decQuadFromInt32(&mut scale, -(*digits as i32));
757                    decQuadScaleB(&mut self.fraction, &self.fraction, &scale, &mut ctx);
758                }
759            }
760            Fraction(frac) => {
761                let ion_frac = IonDecimalPtr::try_from_bigdecimal(frac)?;
762                // we don't support converting from a decimal that cannot fit in a decQuad
763                if ion_frac.type_ != ION_DECIMAL_TYPE_ION_DECIMAL_TYPE_QUAD {
764                    return Err(IonCError::with_additional(
765                        ion_error_code_IERR_INVALID_TIMESTAMP,
766                        "Precision exceeds decQuad for Timestamp",
767                    ));
768                }
769                unsafe {
770                    self.fraction = (*ion_frac).value.quad_value;
771                }
772            }
773        }
774
775        Ok(())
776    }
777
778    /// Converts this `ION_TIMESTAMP` into a `IonDateTime`.
779    ///
780    /// Note that this borrows mutably, because all of the underlying
781    /// `ion_timestamp_*` functions require a mutable pointer, but this operation
782    /// does not actually change the value.
783    pub fn try_to_iondt(&mut self) -> IonCResult<IonDateTime> {
784        let mut prec = 0;
785        ionc!(ion_timestamp_get_precision(self, &mut prec))?;
786        // XXX all of our constants are small u32--so cast over to work with them
787        let prec = prec as u32;
788
789        // we need an offset to construct the date with chrono
790        let mut offset_minutes = 0;
791        let mut has_offset = 0;
792
793        if prec >= ION_TS_MIN {
794            ionc!(ion_timestamp_has_local_offset(self, &mut has_offset))?;
795            if has_offset != 0 {
796                ionc!(ion_timestamp_get_local_offset(self, &mut offset_minutes))?;
797            }
798        }
799        let offset_seconds = (offset_minutes as i32) * SEC_IN_MINS;
800        let tz = FixedOffset::east_opt(offset_seconds)
801            .ok_or_else(|| IonCError::from(ion_error_code_IERR_INVALID_STATE))?;
802        let ts_offset = if has_offset != 0 {
803            TSOffsetKind::KnownOffset
804        } else {
805            TSOffsetKind::UnknownOffset
806        };
807
808        let day = if prec >= ION_TS_DAY { self.day } else { 1 };
809        let month = if prec >= ION_TS_MONTH { self.month } else { 1 };
810        match tz.ymd_opt(self.year as i32, month as u32, day as u32) {
811            LocalResult::Single(date) => {
812                let mut hours = 0;
813                let mut minutes = 0;
814                let mut seconds = 0;
815                if prec >= ION_TS_MIN {
816                    hours = self.hours;
817                    minutes = self.minutes;
818                    seconds = self.seconds;
819                }
820
821                // convert fractional seconds to nanoseconds
822                let frac_seconds = if prec >= ION_TS_FRAC {
823                    IonDecimalPtr::try_from_decquad(self.fraction)?.try_to_bigdecimal()?
824                } else {
825                    BigDecimal::zero()
826                };
827                if frac_seconds < BigDecimal::zero() || frac_seconds >= BigDecimal::from(1) {
828                    return Err(IonCError::from(ion_error_code_IERR_INVALID_STATE));
829                }
830                let nanos = (&frac_seconds * BigDecimal::from(NS_IN_SEC))
831                    .abs()
832                    .to_u32()
833                    .unwrap();
834
835                let dt = date
836                    .and_hms_nano_opt(hours as u32, minutes as u32, seconds as u32, nanos)
837                    .ok_or_else(|| {
838                        IonCError::with_additional(
839                            ion_error_code_IERR_INVALID_TIMESTAMP,
840                            "Could not create DateTime",
841                        )
842                    })?;
843
844                let ts_prec = match prec {
845                    ION_TS_YEAR => Year,
846                    ION_TS_MONTH => Month,
847                    ION_TS_DAY => Day,
848                    ION_TS_MIN => Minute,
849                    ION_TS_SEC => Second,
850                    ION_TS_FRAC => {
851                        let (_, exponent) = frac_seconds.as_bigint_and_exponent();
852                        let mantissa = if exponent <= TS_MAX_MANTISSA_DIGITS {
853                            Digits(exponent as u32)
854                        } else {
855                            // preserve the fractional seconds in the precision.
856                            Fraction(frac_seconds)
857                        };
858                        Fractional(mantissa)
859                    }
860                    _ => {
861                        return Err(IonCError::with_additional(
862                            ion_error_code_IERR_INVALID_TIMESTAMP,
863                            "Invalid ION_TIMESTAMP precision",
864                        ))
865                    }
866                };
867
868                Ok(IonDateTime::new(dt, ts_prec, ts_offset))
869            }
870            _ => Err(IonCError::with_additional(
871                ion_error_code_IERR_INVALID_TIMESTAMP,
872                "Could not create Date",
873            )),
874        }
875    }
876}
877
878#[cfg(test)]
879mod test_timestamp {
880    use crate::result::*;
881    use crate::timestamp::*;
882    use crate::*;
883
884    use rstest::rstest;
885    use rstest_reuse::{self, *};
886
887    use std::ffi::CString;
888    use std::os::raw::c_char;
889    use std::str;
890
891    use chrono::DateTime;
892
893    fn frac(lit: &str) -> Mantissa {
894        Fraction(BigDecimal::parse_bytes(lit.as_bytes(), 10).unwrap())
895    }
896
897    #[template]
898    #[rstest(
899        ion_lit,
900        iso_lit,
901        precision,
902        offset_kind,
903        case::ts_2020("2020T", "2020-01-01T00:00:00Z", Year, UnknownOffset),
904        case::ts_1901_04("1901-04T", "1901-04-01T00:00:00Z", Month, UnknownOffset),
905        case::ts_2014_10_10("2014-10-10", "2014-10-10T00:00:00Z", Day, UnknownOffset),
906        case::ts_2017_07_07T01_20(
907            "2017-07-07T01:20-00:00",
908            "2017-07-07T01:20:00Z",
909            Minute,
910            UnknownOffset,
911        ),
912        case::ts_2018_06_30T03_25_p07_30(
913            "2018-06-30T03:25+07:30",
914            "2018-06-30T03:25:00+07:30",
915            Minute,
916            KnownOffset,
917        ),
918        case::ts_2019_04_30T03_25_32(
919            "2019-04-30T03:25:32-00:00",
920            "2019-04-30T03:25:32Z",
921            Second,
922            UnknownOffset,
923        ),
924        case::ts_2016_03_30T03_25_32_n00_00_same_as_zulu(
925            "2016-03-30T03:25:32-00:00",
926            "2016-03-30T03:25:32Z",
927            Second,
928            UnknownOffset,
929        ),
930        case::ts_2016_03_30T03_25_32_n08_00(
931            "2016-03-30T03:25:32-08:00",
932            "2016-03-30T03:25:32-08:00",
933            Second,
934            KnownOffset,
935        ),
936        case::ts_1975_02_19T12_43_55_123_n08_00(
937            "1975-02-19T12:43:55.123-08:00",
938            "1975-02-19T12:43:55.123-08:00",
939            Fractional(Digits(3)),
940            KnownOffset,
941        ),
942        case::ts_1975_02_19T12_43_55_001_n08_00(
943            "1975-02-19T12:43:55.001-08:00",
944            "1975-02-19T12:43:55.001-08:00",
945            Fractional(Digits(3)),
946            KnownOffset,
947        ),
948        case::ts_1975_02_19T12_43_55_12345_n08_00(
949            "1975-02-19T12:43:55.12345-08:00",
950            "1975-02-19T12:43:55.12345-08:00",
951            Fractional(Digits(5)),
952            KnownOffset,
953        ),
954        case::ts_1975_02_19T12_43_55_00005_n08_00(
955            "1975-02-19T12:43:55.00005-08:00",
956            "1975-02-19T12:43:55.00005-08:00",
957            Fractional(Digits(5)),
958            KnownOffset,
959        ),
960        case::ts_1975_02_19T12_43_55_012345_n08_00(
961            "1975-02-19T12:43:55.012345-08:00",
962            "1975-02-19T12:43:55.012345-08:00",
963            Fractional(Digits(6)),
964            KnownOffset,
965        ),
966        case::ts_1975_02_19T12_43_55_000056_n08_00(
967            "1975-02-19T12:43:55.000056-08:00",
968            "1975-02-19T12:43:55.000056-08:00",
969            Fractional(Digits(6)),
970            KnownOffset,
971        ),
972        case::ts_1974_01_20T12_43_55_123456789_n08_00(
973            "1974-01-20T12:43:55.123456789-08:00",
974            "1974-01-20T12:43:55.123456789-08:00",
975            Fractional(Digits(9)),
976            KnownOffset,
977        ),
978        case::ts_1974_01_20T12_43_55_000056789_n08_00(
979            "1974-01-20T12:43:55.000056789-08:00",
980            "1974-01-20T12:43:55.000056789-08:00",
981            Fractional(Digits(9)),
982            KnownOffset,
983        ),
984        case::ts_1973_12_25T12_43_55_123456789123456789_n08_00_truncation(
985            "1973-12-25T12:43:55.123456789123456789-08:00",
986            "1973-12-25T12:43:55.123456789-08:00",
987            Fractional(frac("0.123456789123456789")),
988            KnownOffset,
989        )
990    )]
991    fn timestamp(ion_lit: &str, iso_lit: &str, precision: TSPrecision, offset_kind: TSOffsetKind) {}
992
993    #[apply(timestamp)]
994    fn assign_from_datetime(
995        ion_lit: &str,
996        iso_lit: &str,
997        precision: TSPrecision,
998        offset_kind: TSOffsetKind,
999    ) -> IonCResult<()> {
1000        // assign into an ION_TIMESTAMP
1001        let dt = DateTime::parse_from_rfc3339(iso_lit).unwrap();
1002        let ion_dt = IonDateTime::try_new(dt, precision, offset_kind)?;
1003        let mut ion_timestamp = ION_TIMESTAMP::default();
1004        ion_timestamp.try_assign_from_iondt(&ion_dt)?;
1005
1006        // serialize
1007        let mut ctx = make_context();
1008        let mut buf = vec![0u8; 128usize];
1009        let mut read = 0;
1010        ionc!(ion_timestamp_to_string(
1011            &mut ion_timestamp,
1012            buf.as_mut_ptr() as *mut c_char,
1013            buf.len().try_into()?,
1014            &mut read,
1015            &mut ctx
1016        ))?;
1017        assert_eq!(
1018            ion_lit,
1019            str::from_utf8(&buf[0..read.try_into()?])?,
1020            "Compare timestamp text serialization"
1021        );
1022
1023        Ok(())
1024    }
1025
1026    #[apply(timestamp)]
1027    fn try_to_datetime(
1028        ion_lit: &str,
1029        iso_lit: &str,
1030        precision: TSPrecision,
1031        offset_kind: TSOffsetKind,
1032    ) -> IonCResult<()> {
1033        let c_ion_lit_owned = CString::new(ion_lit).unwrap();
1034        let c_ion_lit = c_ion_lit_owned.as_bytes_with_nul();
1035        // construct ION_TIMESTAMP from test vector
1036        let mut ion_timestamp = ION_TIMESTAMP::default();
1037        let mut read = 0;
1038        let mut ctx = make_context();
1039        ionc!(ion_timestamp_parse(
1040            &mut ion_timestamp,
1041            c_ion_lit.as_ptr() as *mut c_char,
1042            c_ion_lit.len().try_into()?,
1043            &mut read,
1044            &mut ctx,
1045        ))?;
1046        assert_eq!(
1047            ion_lit.len(),
1048            read.try_into()?,
1049            "Test that we parsed the entire Ion timestamp literal"
1050        );
1051
1052        let expected_dt = DateTime::parse_from_rfc3339(iso_lit).unwrap();
1053        let ion_dt = ion_timestamp.try_to_iondt()?;
1054        assert_eq!(
1055            &expected_dt,
1056            ion_dt.as_datetime(),
1057            "Test that our converted timestamp is equivalent"
1058        );
1059        assert_eq!(
1060            &precision,
1061            ion_dt.precision(),
1062            "Compare timestamp precision"
1063        );
1064        assert_eq!(offset_kind, ion_dt.offset_kind(), "Compare offset kind");
1065
1066        Ok(())
1067    }
1068}
1069
1070impl ION_STRING {
1071    /// Constructs an `ION_STRING` from a `&mut str`.
1072    ///
1073    /// Note that this is effectively Ion C's `&mut str` type so lifetime is managed
1074    /// manually by the caller.
1075    ///
1076    /// Also note, that it is possible to violate the UTF-8 invariant of the source
1077    /// data, so care should be taken when using this API.
1078    ///
1079    /// ## Usage
1080    /// Generally, using a mutable owned source will be the safest option.
1081    /// ```
1082    /// # use ion_c_sys::ION_STRING;
1083    /// # use ion_c_sys::result::IonCResult;
1084    /// # fn main() -> IonCResult<()> {
1085    /// let mut buf = "Some data".to_string();
1086    /// let mut ion_str = ION_STRING::try_from_mut_str(buf.as_mut_str())?;
1087    /// # Ok(())
1088    /// # }
1089    /// ```
1090    #[inline]
1091    pub fn try_from_mut_str(src: &mut str) -> IonCResult<Self> {
1092        unsafe { Self::try_from_mut_bytes(src.as_bytes_mut()) }
1093    }
1094
1095    /// Internal function to coerce an immutable slice to an `ION_STRING`.
1096    ///
1097    /// Inherently unsafe and can only be used with APIs that guarantee immutability.
1098    #[inline]
1099    fn try_from_str(src: &str) -> IonCResult<Self> {
1100        Ok(Self {
1101            value: src.as_ptr() as *mut u8,
1102            length: src.len().try_into()?,
1103        })
1104    }
1105
1106    /// Constructs an `ION_STRING` from a `&mut [u8]`.
1107    ///
1108    /// Note that this is effectively Ion C's `&mut [u8]` type so lifetime is managed
1109    /// manually by the caller.
1110    ///
1111    /// ## Usage
1112    /// Generally, using a mutable owned source will be the safest option.
1113    /// ```
1114    /// # use ion_c_sys::ION_STRING;
1115    /// # use ion_c_sys::result::IonCResult;
1116    /// # fn main() -> IonCResult<()> {
1117    /// let mut buf = b"Some data".to_vec();
1118    /// let mut ion_str = ION_STRING::try_from_mut_bytes(buf.as_mut_slice())?;
1119    /// # Ok(())
1120    /// # }
1121    /// ```
1122    #[inline]
1123    pub fn try_from_mut_bytes(src: &mut [u8]) -> IonCResult<Self> {
1124        Ok(ION_STRING {
1125            value: src.as_mut_ptr(),
1126            length: src.len().try_into()?,
1127        })
1128    }
1129
1130    /// Retrieves a UTF-8 slice view from an `ION_STRING`.
1131    ///
1132    /// When the `value` pointer is `null`, the conversion will fail:
1133    /// ```
1134    /// # use ion_c_sys::*;
1135    /// let ion_str = ION_STRING::default();
1136    /// match ion_str.try_as_str() {
1137    ///     Ok(_) => panic!("Cannot happen!"),
1138    ///     Err(e) => assert_eq!(ion_error_code_IERR_NULL_VALUE, e.code),
1139    /// }
1140    /// ```
1141    ///
1142    /// When the string is not valid UTF-8, the conversion will fail:
1143    /// ```
1144    /// # use ion_c_sys::*;
1145    /// let mut buf = b"\xFF".to_vec();
1146    /// let ion_str = ION_STRING::try_from_mut_bytes(buf.as_mut_slice()).unwrap();
1147    /// match ion_str.try_as_str() {
1148    ///     Ok(_) => panic!("Cannot happen!"),
1149    ///     Err(e) => assert_eq!(ion_error_code_IERR_INVALID_UTF8, e.code),
1150    /// }
1151    /// ```
1152    #[inline]
1153    pub fn try_as_str(&self) -> IonCResult<&str> {
1154        Ok(str::from_utf8(self.try_as_bytes()?)?)
1155    }
1156
1157    /// Retrieves a slice view from an `ION_STRING`
1158    ///
1159    /// When the `value` pointer is `null`, the conversion will return an `IonCError`:
1160    /// ```
1161    /// # use ion_c_sys::*;
1162    /// let ion_str = ION_STRING::default();
1163    /// match ion_str.try_as_bytes() {
1164    ///     Ok(_) => panic!("Cannot happen!"),
1165    ///     Err(e) => assert_eq!(ion_error_code_IERR_NULL_VALUE, e.code),
1166    /// }
1167    /// ```
1168    #[inline]
1169    pub fn try_as_bytes<'a>(&'a self) -> IonCResult<&'a [u8]> {
1170        self.as_bytes(PhantomData::<&'a u8>::default())
1171    }
1172
1173    /// Low-level conversion into an str reference tied to the given owner without UTF-8 validation
1174    fn as_str<'a>(&self, life: PhantomData<&'a u8>) -> IonCResult<&'a str> {
1175        unsafe {
1176            let raw_slice: &'a [u8] = self.as_bytes(life)?;
1177            // Better make sure this came from an Ion C call that checks this
1178            let str_slice = str::from_utf8_unchecked(raw_slice);
1179            Ok(str_slice)
1180        }
1181    }
1182
1183    /// Low-level conversion into a slice associated with a given owner
1184    fn as_bytes<'a>(&self, _life: PhantomData<&'a u8>) -> IonCResult<&'a [u8]> {
1185        // note that we need to build the str slice at a very low level
1186        // to tie the lifetime to the reader
1187        if self.value.is_null() {
1188            Err(IonCError::from(ion_error_code_IERR_NULL_VALUE))
1189        } else {
1190            unsafe {
1191                let u8_slice = slice::from_raw_parts(self.value, self.length.try_into()?);
1192                Ok(u8_slice)
1193            }
1194        }
1195    }
1196}
1197
1198/// Generates easier to use constants for `ION_TYPE`
1199/// These exist as C macros in `ion_types.h` that don't get translated over from `bindgen`.
1200///
1201/// Using `ion_types!(NULL)` will generate a constant of the form:
1202/// ```
1203/// # use ion_c_sys::*;
1204/// pub const ION_TYPE_NULL: *mut ion_type = tid_NULL_INT as *mut ion_type;
1205/// ```
1206macro_rules! ion_types {
1207    ( $($name:ident),* ) => {
1208        $(
1209            paste! {
1210                pub const [<ION_TYPE_ $name:upper>]: *mut ion_type =
1211                    [<tid_ $name _INT>] as *mut ion_type;
1212            }
1213        )*
1214    };
1215}
1216
1217ion_types!(
1218    none, EOF, NULL, BOOL, INT, FLOAT, DECIMAL, TIMESTAMP, SYMBOL, STRING, CLOB, BLOB, LIST, SEXP,
1219    STRUCT, DATAGRAM
1220);