// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.

//! Helpers for implementing `FromOcamlRep::from_ocamlrep` or
//! `FromOcamlRepIn::from_ocamlrep_in`.

use bumpalo::Bump;

use crate::Block;
use crate::FromError;
use crate::FromOcamlRep;
use crate::FromOcamlRepIn;
use crate::Value;

pub fn expect_int(value: Value<'_>) -> Result<isize, FromError> {
    match value.as_int() {
        Some(value) => Ok(value),
        None => Err(FromError::ExpectedInt(value.to_bits())),
    }
}

pub fn expect_nullary_variant(value: Value<'_>, max: usize) -> Result<isize, FromError> {
    let value = expect_int(value)?;
    let max_as_isize: isize = max.try_into().unwrap();
    if 0 <= value && value <= max_as_isize {
        Ok(value)
    } else {
        Err(FromError::NullaryVariantTagOutOfRange { max, actual: value })
    }
}

pub fn expect_block<'a>(value: Value<'a>) -> Result<Block<'a>, FromError> {
    match value.as_block() {
        Some(block) => Ok(block),
        None => Err(FromError::ExpectedBlock(value.as_int().unwrap())),
    }
}

pub fn expect_block_size(block: Block<'_>, size: usize) -> Result<(), FromError> {
    if block.size() != size {
        return Err(FromError::WrongBlockSize {
            expected: size,
            actual: block.size(),
        });
    }
    Ok(())
}

pub fn expect_block_tag(block: Block<'_>, tag: u8) -> Result<(), FromError> {
    if block.tag() != tag {
        return Err(FromError::ExpectedBlockTag {
            expected: tag,
            actual: block.tag(),
        });
    }
    Ok(())
}

pub fn expect_block_with_size_and_tag(
    value: Value<'_>,
    size: usize,
    tag: u8,
) -> Result<Block<'_>, FromError> {
    let block = expect_block(value)?;
    expect_block_size(block, size)?;
    expect_block_tag(block, tag)?;
    Ok(block)
}

pub fn expect_tuple<'a>(value: Value<'a>, size: usize) -> Result<Block<'a>, FromError> {
    let block = expect_block(value)?;
    expect_block_size(block, size)?;
    if block.tag() != 0 {
        return Err(FromError::ExpectedZeroTag(block.tag()));
    }
    Ok(block)
}

pub fn field<T: FromOcamlRep>(block: Block<'_>, field: usize) -> Result<T, FromError> {
    T::from_ocamlrep(block[field]).map_err(|e| FromError::ErrorInField(field, Box::new(e)))
}

pub fn field_in<'a, T: FromOcamlRepIn<'a>>(
    block: Block<'_>,
    field: usize,
    alloc: &'a Bump,
) -> Result<T, FromError> {
    T::from_ocamlrep_in(block[field], alloc)
        .map_err(|e| FromError::ErrorInField(field, Box::new(e)))
}


// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.

use std::borrow::Borrow;
use std::borrow::Cow;
use std::cell::Cell;
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::hash::BuildHasher;
use std::hash::Hash;
use std::mem::size_of;
use std::path::Path;
use std::path::PathBuf;
use std::rc::Rc;
use std::sync::Arc;

use bstr::BStr;
use bstr::BString;
use bumpalo::Bump;
use indexmap::IndexMap;
use indexmap::IndexSet;

use crate::block;
use crate::from;
use crate::Allocator;
use crate::FromError;
use crate::FromOcamlRep;
use crate::FromOcamlRepIn;
use crate::ToOcamlRep;
use crate::Value;

macro_rules! trivial_from_in_impl {
    ($ty:ty) => {
        impl<'a> FromOcamlRepIn<'a> for $ty {
            fn from_ocamlrep_in(value: Value<'_>, _alloc: &'a Bump) -> Result<Self, FromError> {
                Self::from_ocamlrep(value)
            }
        }
    };
}

impl ToOcamlRep for () {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, _alloc: &'a A) -> Value<'a> {
        Value::int(0)
    }
}

impl FromOcamlRep for () {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        match from::expect_int(value)? {
            0 => Ok(()),
            x => Err(FromError::ExpectedUnit(x)),
        }
    }
}

trivial_from_in_impl!(());

impl ToOcamlRep for isize {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, _alloc: &'a A) -> Value<'a> {
        Value::int(*self)
    }
}

impl FromOcamlRep for isize {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        from::expect_int(value)
    }
}

trivial_from_in_impl!(isize);

impl ToOcamlRep for usize {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, _alloc: &'a A) -> Value<'a> {
        Value::int((*self).try_into().unwrap())
    }
}

impl FromOcamlRep for usize {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        Ok(from::expect_int(value)?.try_into()?)
    }
}

trivial_from_in_impl!(usize);

impl ToOcamlRep for i64 {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, _alloc: &'a A) -> Value<'a> {
        Value::int((*self).try_into().unwrap())
    }
}

impl FromOcamlRep for i64 {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        Ok(from::expect_int(value)?.try_into()?)
    }
}

trivial_from_in_impl!(i64);

impl ToOcamlRep for u64 {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, _alloc: &'a A) -> Value<'a> {
        Value::int((*self).try_into().unwrap())
    }
}

impl FromOcamlRep for u64 {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        Ok(from::expect_int(value)?.try_into()?)
    }
}

trivial_from_in_impl!(u64);

impl ToOcamlRep for i32 {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, _alloc: &'a A) -> Value<'a> {
        Value::int((*self).try_into().unwrap())
    }
}

impl FromOcamlRep for i32 {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        Ok(from::expect_int(value)?.try_into()?)
    }
}

trivial_from_in_impl!(i32);

impl ToOcamlRep for u32 {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, _alloc: &'a A) -> Value<'a> {
        Value::int((*self).try_into().unwrap())
    }
}

impl FromOcamlRep for u32 {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        Ok(from::expect_int(value)?.try_into()?)
    }
}

trivial_from_in_impl!(u32);

impl ToOcamlRep for bool {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, _alloc: &'a A) -> Value<'a> {
        Value::int((*self).into())
    }
}

impl FromOcamlRep for bool {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        match from::expect_int(value)? {
            0 => Ok(false),
            1 => Ok(true),
            x => Err(FromError::ExpectedBool(x)),
        }
    }
}

trivial_from_in_impl!(bool);

impl ToOcamlRep for char {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, _alloc: &'a A) -> Value<'a> {
        assert!(*self as u32 <= 255, "char out of range: {}", self);
        Value::int(*self as isize)
    }
}

impl FromOcamlRep for char {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        let c = from::expect_int(value)?;
        if (0..=255).contains(&c) {
            Ok(c as u8 as char)
        } else {
            Err(FromError::ExpectedChar(c))
        }
    }
}

trivial_from_in_impl!(char);

impl ToOcamlRep for f64 {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        let mut block = alloc.block_with_size_and_tag(1, block::DOUBLE_TAG);
        alloc.set_field(&mut block, 0, unsafe {
            Value::from_bits(self.to_bits() as usize)
        });
        block.build()
    }
}

impl FromOcamlRep for f64 {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        let block = from::expect_block_with_size_and_tag(value, 1, block::DOUBLE_TAG)?;
        Ok(f64::from_bits(block[0].0 as u64))
    }
}

trivial_from_in_impl!(f64);

impl<T: ToOcamlRep + Sized> ToOcamlRep for Box<T> {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        alloc.add(&**self)
    }
}

impl<T: FromOcamlRep + Sized> FromOcamlRep for Box<T> {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        Ok(Box::new(T::from_ocamlrep(value)?))
    }
}

impl<T: ToOcamlRep + Sized> ToOcamlRep for &'_ T {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        alloc.memoized(
            *self as *const T as *const usize as usize,
            size_of::<T>(),
            |alloc| (**self).to_ocamlrep(alloc),
        )
    }
}

impl<'a, T: FromOcamlRepIn<'a>> FromOcamlRepIn<'a> for &'a T {
    fn from_ocamlrep_in(value: Value<'_>, alloc: &'a Bump) -> Result<Self, FromError> {
        // NB: We don't get any sharing this way.
        Ok(alloc.alloc(T::from_ocamlrep_in(value, alloc)?))
    }
}

impl<T: ToOcamlRep + Sized> ToOcamlRep for Rc<T> {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        alloc.memoized(
            self.as_ref() as *const T as usize,
            size_of::<T>(),
            |alloc| alloc.add(self.as_ref()),
        )
    }
}

impl<T: FromOcamlRep> FromOcamlRep for Rc<T> {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        // NB: We don't get any sharing this way.
        Ok(Rc::new(T::from_ocamlrep(value)?))
    }
}

impl<T: ToOcamlRep + Sized> ToOcamlRep for Arc<T> {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        alloc.memoized(
            self.as_ref() as *const T as usize,
            size_of::<T>(),
            |alloc| alloc.add(self.as_ref()),
        )
    }
}

impl<T: FromOcamlRep> FromOcamlRep for Arc<T> {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        // NB: We don't get any sharing this way.
        Ok(Arc::new(T::from_ocamlrep(value)?))
    }
}

impl<T: ToOcamlRep> ToOcamlRep for RefCell<T> {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        let mut block = alloc.block_with_size(1);
        let value_ref: std::cell::Ref<'a, T> = self.borrow();
        alloc.set_field(&mut block, 0, alloc.add(&*value_ref));
        // SAFETY: the `&'a self` lifetime is intended to ensure that our `T` is
        // not mutated or dropped during the to-OCaml conversion, in order to
        // ensure that the allocator's memoization table isn't invalidated. We
        // can't guarantee that statically for types with internal mutability,
        // so the `ToOcamlRep` docs ask the caller to promise not to mutate or
        // drop these values. If they violate that requirement, the allocator
        // may give stale results in the event of aliasing, which is definitely
        // undesirable, but does not break type safety on the Rust side. The
        // allocator ties the lifetime of the Value we're returning to our
        // local variable `value_ref`, but it doesn't actually reference that
        // local, so it's safe to cast the lifetime away.
        unsafe { std::mem::transmute::<Value<'_>, Value<'a>>(block.build()) }
    }
}

impl<T: Copy + ToOcamlRep> ToOcamlRep for Cell<T> {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        let mut block = alloc.block_with_size(1);
        let value_copy = self.get();
        alloc.set_field(&mut block, 0, alloc.add(&value_copy));
        // SAFETY: as above with RefCell, we need to cast away the lifetime to
        // deal with internal mutability.
        unsafe { std::mem::transmute::<Value<'_>, Value<'a>>(block.build()) }
    }
}

impl<T: FromOcamlRep> FromOcamlRep for Cell<T> {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        let block = from::expect_tuple(value, 1)?;
        let value: T = from::field(block, 0)?;
        Ok(Cell::new(value))
    }
}

impl<'a, T: FromOcamlRepIn<'a>> FromOcamlRepIn<'a> for Cell<T> {
    fn from_ocamlrep_in(value: Value<'_>, alloc: &'a Bump) -> Result<Self, FromError> {
        let block = from::expect_tuple(value, 1)?;
        let value: T = from::field_in(block, 0, alloc)?;
        Ok(Cell::new(value))
    }
}

impl<T: FromOcamlRep> FromOcamlRep for RefCell<T> {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        let block = from::expect_tuple(value, 1)?;
        let value: T = from::field(block, 0)?;
        Ok(RefCell::new(value))
    }
}

impl<'a, T: FromOcamlRepIn<'a>> FromOcamlRepIn<'a> for RefCell<T> {
    fn from_ocamlrep_in(value: Value<'_>, alloc: &'a Bump) -> Result<Self, FromError> {
        let block = from::expect_tuple(value, 1)?;
        let value: T = from::field_in(block, 0, alloc)?;
        Ok(RefCell::new(value))
    }
}

impl<T: ToOcamlRep> ToOcamlRep for Option<T> {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        match self {
            None => Value::int(0),
            Some(val) => {
                let mut block = alloc.block_with_size(1);
                alloc.set_field(&mut block, 0, alloc.add(val));
                block.build()
            }
        }
    }
}

impl<T: FromOcamlRep> FromOcamlRep for Option<T> {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        if value.is_int() {
            let _ = from::expect_nullary_variant(value, 0)?;
            Ok(None)
        } else {
            let block = from::expect_block_with_size_and_tag(value, 1, 0)?;
            Ok(Some(from::field(block, 0)?))
        }
    }
}

impl<'a, T: FromOcamlRepIn<'a>> FromOcamlRepIn<'a> for Option<T> {
    fn from_ocamlrep_in(value: Value<'_>, alloc: &'a Bump) -> Result<Self, FromError> {
        if value.is_int() {
            let _ = from::expect_nullary_variant(value, 0)?;
            Ok(None)
        } else {
            let block = from::expect_block_with_size_and_tag(value, 1, 0)?;
            Ok(Some(from::field_in(block, 0, alloc)?))
        }
    }
}

impl<T: ToOcamlRep, E: ToOcamlRep> ToOcamlRep for Result<T, E> {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        match self {
            Ok(val) => {
                let mut block = alloc.block_with_size(1);
                alloc.set_field(&mut block, 0, alloc.add(val));
                block.build()
            }
            Err(val) => {
                let mut block = alloc.block_with_size_and_tag(1, 1);
                alloc.set_field(&mut block, 0, alloc.add(val));
                block.build()
            }
        }
    }
}

impl<T: FromOcamlRep, E: FromOcamlRep> FromOcamlRep for Result<T, E> {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        let block = from::expect_block(value)?;
        match block.tag() {
            0 => Ok(Ok(from::field(block, 0)?)),
            1 => Ok(Err(from::field(block, 0)?)),
            t => Err(FromError::BlockTagOutOfRange { max: 1, actual: t }),
        }
    }
}

impl<'a, T: FromOcamlRepIn<'a>, E: FromOcamlRepIn<'a>> FromOcamlRepIn<'a> for Result<T, E> {
    fn from_ocamlrep_in(value: Value<'_>, alloc: &'a Bump) -> Result<Self, FromError> {
        let block = from::expect_block(value)?;
        match block.tag() {
            0 => Ok(Ok(from::field_in(block, 0, alloc)?)),
            1 => Ok(Err(from::field_in(block, 0, alloc)?)),
            t => Err(FromError::BlockTagOutOfRange { max: 1, actual: t }),
        }
    }
}

impl<T: ToOcamlRep> ToOcamlRep for [T] {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        let mut hd = alloc.add(&());
        for val in self.iter().rev() {
            let mut block = alloc.block_with_size(2);
            alloc.set_field(&mut block, 0, alloc.add(val));
            alloc.set_field(&mut block, 1, hd);
            hd = block.build();
        }
        hd
    }
}

impl<T: ToOcamlRep> ToOcamlRep for &'_ [T] {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        alloc.memoized(
            self.as_ptr() as usize,
            self.len() * size_of::<T>(),
            |alloc| (**self).to_ocamlrep(alloc),
        )
    }
}

impl<'a, T: FromOcamlRepIn<'a>> FromOcamlRepIn<'a> for &'a [T] {
    fn from_ocamlrep_in(value: Value<'_>, alloc: &'a Bump) -> Result<Self, FromError> {
        let mut len = 0usize;
        let mut hd = value;
        while !hd.is_int() {
            let block = from::expect_tuple(hd, 2)?;
            len += 1;
            hd = block[1];
        }
        let hd = hd.as_int().unwrap();
        if hd != 0 {
            return Err(FromError::ExpectedUnit(hd));
        }

        let mut vec = bumpalo::collections::Vec::with_capacity_in(len, alloc);
        let mut hd = value;
        while !hd.is_int() {
            let block = from::expect_tuple(hd, 2).unwrap();
            vec.push(from::field_in(block, 0, alloc)?);
            hd = block[1];
        }
        Ok(vec.into_bump_slice())
    }
}

impl<T: ToOcamlRep> ToOcamlRep for Box<[T]> {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        (**self).to_ocamlrep(alloc)
    }
}

impl<T: FromOcamlRep> FromOcamlRep for Box<[T]> {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        let vec = <Vec<T>>::from_ocamlrep(value)?;
        Ok(vec.into_boxed_slice())
    }
}

impl<T: ToOcamlRep> ToOcamlRep for Vec<T> {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        alloc.add(self.as_slice())
    }
}

impl<T: FromOcamlRep> FromOcamlRep for Vec<T> {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        let mut vec = vec![];
        let mut hd = value;
        while !hd.is_int() {
            let block = from::expect_tuple(hd, 2)?;
            vec.push(from::field(block, 0)?);
            hd = block[1];
        }
        let hd = hd.as_int().unwrap();
        if hd != 0 {
            return Err(FromError::ExpectedUnit(hd));
        }
        Ok(vec)
    }
}

impl<'a, T: FromOcamlRep> FromOcamlRepIn<'a> for Vec<T> {
    fn from_ocamlrep_in(value: Value<'_>, _alloc: &'a Bump) -> Result<Self, FromError> {
        Self::from_ocamlrep(value)
    }
}

impl<K: ToOcamlRep + Ord, V: ToOcamlRep> ToOcamlRep for BTreeMap<K, V> {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        if self.is_empty() {
            return Value::int(0);
        }
        let len = self.len();
        let mut iter = self
            .iter()
            .map(|(k, v)| (k.to_ocamlrep(alloc), v.to_ocamlrep(alloc)));
        let (res, _) = sorted_iter_to_ocaml_map(&mut iter, alloc, len);
        res
    }
}

impl<K: FromOcamlRep + Ord, V: FromOcamlRep> FromOcamlRep for BTreeMap<K, V> {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        let mut map = BTreeMap::new();
        btree_map_from_ocamlrep(&mut map, value)?;
        Ok(map)
    }
}

impl<'a, K: FromOcamlRep + Ord, V: FromOcamlRep> FromOcamlRepIn<'a> for BTreeMap<K, V> {
    fn from_ocamlrep_in(value: Value<'_>, _alloc: &'a Bump) -> Result<Self, FromError> {
        Self::from_ocamlrep(value)
    }
}

/// Given an iterator which emits key-value pairs (already converted to OCaml
/// values), build an OCaml Map containing those bindings. The iterator must
/// emit each key only once. The key-value pairs must be emitted in ascending
/// order, sorted by key. The iterator must emit exactly `size` pairs.
pub fn sorted_iter_to_ocaml_map<'a, A: Allocator>(
    iter: &mut impl Iterator<Item = (Value<'a>, Value<'a>)>,
    alloc: &'a A,
    size: usize,
) -> (Value<'a>, usize) {
    if size == 0 {
        return (Value::int(0), 0);
    }
    let (left, left_height) = sorted_iter_to_ocaml_map(iter, alloc, size / 2);
    let (key, val) = iter.next().unwrap();
    let (right, right_height) = sorted_iter_to_ocaml_map(iter, alloc, size - 1 - size / 2);
    let height = std::cmp::max(left_height, right_height) + 1;
    let mut block = alloc.block_with_size(5);
    alloc.set_field(&mut block, 0, left);
    alloc.set_field(&mut block, 1, key);
    alloc.set_field(&mut block, 2, val);
    alloc.set_field(&mut block, 3, right);
    alloc.set_field(&mut block, 4, alloc.add_copy(height));
    (block.build(), height)
}

fn btree_map_from_ocamlrep<K: FromOcamlRep + Ord, V: FromOcamlRep>(
    map: &mut BTreeMap<K, V>,
    value: Value<'_>,
) -> Result<(), FromError> {
    if value.is_int() {
        let _ = from::expect_nullary_variant(value, 0)?;
        return Ok(());
    }
    let block = from::expect_block_with_size_and_tag(value, 5, 0)?;
    btree_map_from_ocamlrep(map, block[0])?;
    let key: K = from::field(block, 1)?;
    let val: V = from::field(block, 2)?;
    map.insert(key, val);
    btree_map_from_ocamlrep(map, block[3])?;
    Ok(())
}

fn vec_from_ocaml_map_impl<K: FromOcamlRep, V: FromOcamlRep>(
    vec: &mut Vec<(K, V)>,
    value: Value<'_>,
) -> Result<(), FromError> {
    if value.is_int() {
        let _ = from::expect_nullary_variant(value, 0)?;
        return Ok(());
    }
    let block = from::expect_block_with_size_and_tag(value, 5, 0)?;
    vec_from_ocaml_map_impl(vec, block[0])?;
    let key: K = from::field(block, 1)?;
    let val: V = from::field(block, 2)?;
    vec.push((key, val));
    vec_from_ocaml_map_impl(vec, block[3])?;
    Ok(())
}

pub fn vec_from_ocaml_map<K: FromOcamlRep, V: FromOcamlRep>(
    value: Value<'_>,
) -> Result<Vec<(K, V)>, FromError> {
    let mut vec = vec![];
    vec_from_ocaml_map_impl(&mut vec, value)?;
    Ok(vec)
}

pub fn vec_from_ocaml_map_in<'a, K, V>(
    value: Value<'_>,
    vec: &mut bumpalo::collections::Vec<'a, (K, V)>,
    alloc: &'a Bump,
) -> Result<(), FromError>
where
    K: FromOcamlRepIn<'a> + Ord,
    V: FromOcamlRepIn<'a>,
{
    if value.is_int() {
        let _ = from::expect_nullary_variant(value, 0)?;
        return Ok(());
    }
    let block = from::expect_block_with_size_and_tag(value, 5, 0)?;
    vec_from_ocaml_map_in(block[0], vec, alloc)?;
    let key: K = from::field_in(block, 1, alloc)?;
    let val: V = from::field_in(block, 2, alloc)?;
    vec.push((key, val));
    vec_from_ocaml_map_in(block[3], vec, alloc)?;
    Ok(())
}

impl<T: ToOcamlRep + Ord> ToOcamlRep for BTreeSet<T> {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        if self.is_empty() {
            return Value::int(0);
        }
        let len = self.len();
        let mut iter = self.iter().map(|x| x.to_ocamlrep(alloc));
        let (res, _) = sorted_iter_to_ocaml_set(&mut iter, alloc, len);
        res
    }
}

impl<T: FromOcamlRep + Ord> FromOcamlRep for BTreeSet<T> {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        let mut set = BTreeSet::new();
        btree_set_from_ocamlrep(&mut set, value)?;
        Ok(set)
    }
}

impl<'a, T: FromOcamlRep + Ord> FromOcamlRepIn<'a> for BTreeSet<T> {
    fn from_ocamlrep_in(value: Value<'_>, _alloc: &'a Bump) -> Result<Self, FromError> {
        Self::from_ocamlrep(value)
    }
}

/// Build an OCaml Set containing all items emitted by the given iterator. The
/// iterator must emit each item only once. The items must be emitted in
/// ascending order. The iterator must emit exactly `size` items.
pub fn sorted_iter_to_ocaml_set<'a, A: Allocator>(
    iter: &mut impl Iterator<Item = Value<'a>>,
    alloc: &'a A,
    size: usize,
) -> (Value<'a>, usize) {
    if size == 0 {
        return (Value::int(0), 0);
    }
    let (left, left_height) = sorted_iter_to_ocaml_set(iter, alloc, size / 2);
    let val = iter.next().unwrap();
    let (right, right_height) = sorted_iter_to_ocaml_set(iter, alloc, size - 1 - size / 2);
    let height = std::cmp::max(left_height, right_height) + 1;
    let mut block = alloc.block_with_size(4);
    alloc.set_field(&mut block, 0, left);
    alloc.set_field(&mut block, 1, val);
    alloc.set_field(&mut block, 2, right);
    alloc.set_field(&mut block, 3, alloc.add_copy(height));
    (block.build(), height)
}

fn btree_set_from_ocamlrep<T: FromOcamlRep + Ord>(
    set: &mut BTreeSet<T>,
    value: Value<'_>,
) -> Result<(), FromError> {
    if value.is_int() {
        let _ = from::expect_nullary_variant(value, 0)?;
        return Ok(());
    }
    let block = from::expect_block_with_size_and_tag(value, 4, 0)?;
    btree_set_from_ocamlrep(set, block[0])?;
    set.insert(from::field(block, 1)?);
    btree_set_from_ocamlrep(set, block[2])?;
    Ok(())
}

fn vec_from_ocaml_set_impl<T: FromOcamlRep>(
    value: Value<'_>,
    vec: &mut Vec<T>,
) -> Result<(), FromError> {
    if value.is_int() {
        let _ = from::expect_nullary_variant(value, 0)?;
        return Ok(());
    }
    let block = from::expect_block_with_size_and_tag(value, 4, 0)?;
    vec_from_ocaml_set_impl(block[0], vec)?;
    vec.push(from::field(block, 1)?);
    vec_from_ocaml_set_impl(block[2], vec)?;
    Ok(())
}

pub fn vec_from_ocaml_set<T: FromOcamlRep>(value: Value<'_>) -> Result<Vec<T>, FromError> {
    let mut vec = vec![];
    vec_from_ocaml_set_impl(value, &mut vec)?;
    Ok(vec)
}

pub fn vec_from_ocaml_set_in<'a, T: FromOcamlRepIn<'a> + Ord>(
    value: Value<'_>,
    vec: &mut bumpalo::collections::Vec<'a, T>,
    alloc: &'a Bump,
) -> Result<(), FromError> {
    if value.is_int() {
        let _ = from::expect_nullary_variant(value, 0)?;
        return Ok(());
    }
    let block = from::expect_block_with_size_and_tag(value, 4, 0)?;
    vec_from_ocaml_set_in(block[0], vec, alloc)?;
    vec.push(from::field_in(block, 1, alloc)?);
    vec_from_ocaml_set_in(block[2], vec, alloc)?;
    Ok(())
}

impl<K: ToOcamlRep + Ord, V: ToOcamlRep, S: BuildHasher + Default> ToOcamlRep
    for IndexMap<K, V, S>
{
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        if self.is_empty() {
            return Value::int(0);
        }
        let mut vec: Vec<(&'a K, &'a V)> = self.iter().collect();
        vec.sort_unstable_by_key(|&(k, _)| k);
        let len = vec.len();
        let mut iter = vec.iter().map(|(k, v)| {
            let k: &'a K = k;
            let v: &'a V = v;
            (k.to_ocamlrep(alloc), v.to_ocamlrep(alloc))
        });
        let (res, _) = sorted_iter_to_ocaml_map(&mut iter, alloc, len);
        res
    }
}

impl<K: FromOcamlRep + Ord + Hash, V: FromOcamlRep, S: BuildHasher + Default> FromOcamlRep
    for IndexMap<K, V, S>
{
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        let vec = vec_from_ocaml_map(value)?;
        Ok(vec.into_iter().collect())
    }
}

impl<T: ToOcamlRep + Ord, S: BuildHasher + Default> ToOcamlRep for IndexSet<T, S> {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        if self.is_empty() {
            return Value::int(0);
        }
        let mut vec: Vec<&'a T> = self.iter().collect();
        vec.sort_unstable();
        let len = vec.len();
        let mut iter = vec.iter().copied().map(|x| x.to_ocamlrep(alloc));
        let (res, _) = sorted_iter_to_ocaml_set(&mut iter, alloc, len);
        res
    }
}

impl<T: FromOcamlRep + Ord + Hash, S: BuildHasher + Default> FromOcamlRep for IndexSet<T, S> {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        let set = <BTreeSet<T>>::from_ocamlrep(value)?;
        Ok(set.into_iter().collect())
    }
}

impl ToOcamlRep for OsStr {
    // TODO: A Windows implementation would be nice, but what does the OCaml
    // runtime do? If we need Windows support, we'll have to find out.
    #[cfg(unix)]
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        use std::os::unix::ffi::OsStrExt;
        alloc.add(self.as_bytes())
    }

    #[cfg(target_arch = "wasm32")]
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        panic!()
    }
}

impl ToOcamlRep for &'_ OsStr {
    #[cfg(unix)]
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        use std::os::unix::ffi::OsStrExt;
        alloc.add(self.as_bytes())
    }

    #[cfg(target_arch = "wasm32")]
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        panic!()
    }
}

impl<'a> FromOcamlRepIn<'a> for &'a OsStr {
    #[cfg(unix)]
    fn from_ocamlrep_in<'b>(value: Value<'b>, alloc: &'a Bump) -> Result<Self, FromError> {
        use std::os::unix::ffi::OsStrExt;
        Ok(std::ffi::OsStr::from_bytes(<&'a [u8]>::from_ocamlrep_in(
            value, alloc,
        )?))
    }

    #[cfg(target_arch = "wasm32")]
    fn from_ocamlrep_in<'b>(value: Value<'b>, alloc: &'a Bump) -> Result<Self, FromError> {
        panic!()
    }
}

impl ToOcamlRep for OsString {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        alloc.add(self.as_os_str())
    }
}

impl FromOcamlRep for OsString {
    #[cfg(unix)]
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        use std::os::unix::ffi::OsStrExt;
        Ok(OsString::from(std::ffi::OsStr::from_bytes(
            bytes_from_ocamlrep(value)?,
        )))
    }

    #[cfg(target_arch = "wasm32")]
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        panic!()
    }
}

impl ToOcamlRep for Path {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        alloc.add(self.as_os_str())
    }
}

impl ToOcamlRep for &'_ Path {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        alloc.add(self.as_os_str())
    }
}

impl<'a> FromOcamlRepIn<'a> for &'a Path {
    fn from_ocamlrep_in<'b>(value: Value<'b>, alloc: &'a Bump) -> Result<Self, FromError> {
        Ok(Path::new(<&'a OsStr>::from_ocamlrep_in(value, alloc)?))
    }
}

impl ToOcamlRep for PathBuf {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        alloc.add(self.as_os_str())
    }
}

impl FromOcamlRep for PathBuf {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        Ok(PathBuf::from(OsString::from_ocamlrep(value)?))
    }
}

impl ToOcamlRep for String {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        alloc.add(self.as_str())
    }
}

impl FromOcamlRep for String {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        Ok(String::from(str_from_ocamlrep(value)?))
    }
}

trivial_from_in_impl!(String);

impl ToOcamlRep for Cow<'_, str> {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        let s: &str = self.borrow();
        alloc.add(s)
    }
}

impl FromOcamlRep for Cow<'_, str> {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        Ok(Cow::Owned(String::from(str_from_ocamlrep(value)?)))
    }
}

impl ToOcamlRep for str {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        str_to_ocamlrep(self, alloc)
    }
}

impl ToOcamlRep for &'_ str {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        alloc.memoized(self.as_bytes().as_ptr() as usize, self.len(), |alloc| {
            (**self).to_ocamlrep(alloc)
        })
    }
}

impl<'a> FromOcamlRepIn<'a> for &'a str {
    fn from_ocamlrep_in<'b>(value: Value<'b>, alloc: &'a Bump) -> Result<Self, FromError> {
        Ok(alloc.alloc_str(str_from_ocamlrep(value)?))
    }
}

/// Allocate an OCaml string using the given allocator and copy the given string
/// slice into it.
pub fn str_to_ocamlrep<'a, A: Allocator>(s: &str, alloc: &'a A) -> Value<'a> {
    bytes_to_ocamlrep(s.as_bytes(), alloc)
}

/// Given an OCaml string, return a string slice pointing to its contents, if
/// they are valid UTF-8.
pub fn str_from_ocamlrep<'a>(value: Value<'a>) -> Result<&'a str, FromError> {
    Ok(std::str::from_utf8(bytes_from_ocamlrep(value)?)?)
}

impl ToOcamlRep for Vec<u8> {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        alloc.add(self.as_slice())
    }
}

impl FromOcamlRep for Vec<u8> {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        Ok(Vec::from(bytes_from_ocamlrep(value)?))
    }
}

impl ToOcamlRep for BString {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        alloc.add(self.as_slice())
    }
}

impl FromOcamlRep for BString {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        Ok(Vec::from_ocamlrep(value)?.into())
    }
}

impl ToOcamlRep for BStr {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        bytes_to_ocamlrep(self, alloc)
    }
}

impl ToOcamlRep for &'_ BStr {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        alloc.memoized(self.as_ptr() as usize, self.len(), |alloc| {
            (**self).to_ocamlrep(alloc)
        })
    }
}

impl<'a> FromOcamlRepIn<'a> for &'a BStr {
    fn from_ocamlrep_in<'b>(value: Value<'b>, alloc: &'a Bump) -> Result<Self, FromError> {
        let slice: &[u8] = alloc.alloc_slice_copy(bytes_from_ocamlrep(value)?);
        Ok(slice.into())
    }
}

impl ToOcamlRep for [u8] {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        bytes_to_ocamlrep(self, alloc)
    }
}

impl ToOcamlRep for &'_ [u8] {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        alloc.memoized(self.as_ptr() as usize, self.len(), |alloc| {
            (**self).to_ocamlrep(alloc)
        })
    }
}

impl<'a> FromOcamlRepIn<'a> for &'a [u8] {
    fn from_ocamlrep_in<'b>(value: Value<'b>, alloc: &'a Bump) -> Result<Self, FromError> {
        Ok(alloc.alloc_slice_copy(bytes_from_ocamlrep(value)?))
    }
}

/// Allocate an OCaml string using the given allocator and copy the given byte
/// slice into it.
pub fn bytes_to_ocamlrep<'a, A: Allocator>(bytes: &[u8], alloc: &'a A) -> Value<'a> {
    use std::io::Write;
    let mut w = alloc.byte_string_with_len(bytes.len());
    let _ = w.write(bytes).unwrap();
    w.build()
}

/// Given an OCaml string, return a byte slice pointing to its contents.
pub fn bytes_from_ocamlrep<'a>(value: Value<'a>) -> Result<&'a [u8], FromError> {
    let block = from::expect_block(value)?;
    from::expect_block_tag(block, block::STRING_TAG)?;
    let block_size_in_bytes = block.size() * std::mem::size_of::<Value<'_>>();
    let slice = unsafe {
        let ptr = block.0.as_ptr().add(1) as *const u8;
        let padding = *ptr.add(block_size_in_bytes - 1);
        let len = block_size_in_bytes - padding as usize - 1;
        std::slice::from_raw_parts(ptr, len)
    };
    Ok(slice)
}

impl<T0, T1> ToOcamlRep for (T0, T1)
where
    T0: ToOcamlRep,
    T1: ToOcamlRep,
{
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        let mut block = alloc.block_with_size(2);
        alloc.set_field(&mut block, 0, alloc.add(&self.0));
        alloc.set_field(&mut block, 1, alloc.add(&self.1));
        block.build()
    }
}

impl<T0, T1> FromOcamlRep for (T0, T1)
where
    T0: FromOcamlRep,
    T1: FromOcamlRep,
{
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        let block = from::expect_tuple(value, 2)?;
        let f0: T0 = from::field(block, 0)?;
        let f1: T1 = from::field(block, 1)?;
        Ok((f0, f1))
    }
}

impl<'a, T0, T1> FromOcamlRepIn<'a> for (T0, T1)
where
    T0: FromOcamlRepIn<'a>,
    T1: FromOcamlRepIn<'a>,
{
    fn from_ocamlrep_in<'b>(value: Value<'b>, alloc: &'a Bump) -> Result<Self, FromError> {
        let block = from::expect_tuple(value, 2)?;
        let f0: T0 = from::field_in(block, 0, alloc)?;
        let f1: T1 = from::field_in(block, 1, alloc)?;
        Ok((f0, f1))
    }
}

impl<T0, T1, T2> ToOcamlRep for (T0, T1, T2)
where
    T0: ToOcamlRep,
    T1: ToOcamlRep,
    T2: ToOcamlRep,
{
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        let mut block = alloc.block_with_size(3);
        alloc.set_field(&mut block, 0, alloc.add(&self.0));
        alloc.set_field(&mut block, 1, alloc.add(&self.1));
        alloc.set_field(&mut block, 2, alloc.add(&self.2));
        block.build()
    }
}

impl<T0, T1, T2> FromOcamlRep for (T0, T1, T2)
where
    T0: FromOcamlRep,
    T1: FromOcamlRep,
    T2: FromOcamlRep,
{
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        let block = from::expect_tuple(value, 3)?;
        let f0: T0 = from::field(block, 0)?;
        let f1: T1 = from::field(block, 1)?;
        let f2: T2 = from::field(block, 2)?;
        Ok((f0, f1, f2))
    }
}

impl<'a, T0, T1, T2> FromOcamlRepIn<'a> for (T0, T1, T2)
where
    T0: FromOcamlRepIn<'a>,
    T1: FromOcamlRepIn<'a>,
    T2: FromOcamlRepIn<'a>,
{
    fn from_ocamlrep_in<'b>(value: Value<'b>, alloc: &'a Bump) -> Result<Self, FromError> {
        let block = from::expect_tuple(value, 3)?;
        let f0: T0 = from::field_in(block, 0, alloc)?;
        let f1: T1 = from::field_in(block, 1, alloc)?;
        let f2: T2 = from::field_in(block, 2, alloc)?;
        Ok((f0, f1, f2))
    }
}

impl<T0, T1, T2, T3> ToOcamlRep for (T0, T1, T2, T3)
where
    T0: ToOcamlRep,
    T1: ToOcamlRep,
    T2: ToOcamlRep,
    T3: ToOcamlRep,
{
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        let mut block = alloc.block_with_size(4);
        alloc.set_field(&mut block, 0, alloc.add(&self.0));
        alloc.set_field(&mut block, 1, alloc.add(&self.1));
        alloc.set_field(&mut block, 2, alloc.add(&self.2));
        alloc.set_field(&mut block, 3, alloc.add(&self.3));
        block.build()
    }
}

impl<T0, T1, T2, T3> FromOcamlRep for (T0, T1, T2, T3)
where
    T0: FromOcamlRep,
    T1: FromOcamlRep,
    T2: FromOcamlRep,
    T3: FromOcamlRep,
{
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        let block = from::expect_tuple(value, 4)?;
        let f0: T0 = from::field(block, 0)?;
        let f1: T1 = from::field(block, 1)?;
        let f2: T2 = from::field(block, 2)?;
        let f3: T3 = from::field(block, 3)?;
        Ok((f0, f1, f2, f3))
    }
}

impl<'a, T0, T1, T2, T3> FromOcamlRepIn<'a> for (T0, T1, T2, T3)
where
    T0: FromOcamlRepIn<'a>,
    T1: FromOcamlRepIn<'a>,
    T2: FromOcamlRepIn<'a>,
    T3: FromOcamlRepIn<'a>,
{
    fn from_ocamlrep_in<'b>(value: Value<'b>, alloc: &'a Bump) -> Result<Self, FromError> {
        let block = from::expect_tuple(value, 4)?;
        let f0: T0 = from::field_in(block, 0, alloc)?;
        let f1: T1 = from::field_in(block, 1, alloc)?;
        let f2: T2 = from::field_in(block, 2, alloc)?;
        let f3: T3 = from::field_in(block, 3, alloc)?;
        Ok((f0, f1, f2, f3))
    }
}

impl<T0, T1, T2, T3, T4> ToOcamlRep for (T0, T1, T2, T3, T4)
where
    T0: ToOcamlRep,
    T1: ToOcamlRep,
    T2: ToOcamlRep,
    T3: ToOcamlRep,
    T4: ToOcamlRep,
{
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        let mut block = alloc.block_with_size(5);
        alloc.set_field(&mut block, 0, alloc.add(&self.0));
        alloc.set_field(&mut block, 1, alloc.add(&self.1));
        alloc.set_field(&mut block, 2, alloc.add(&self.2));
        alloc.set_field(&mut block, 3, alloc.add(&self.3));
        alloc.set_field(&mut block, 4, alloc.add(&self.4));
        block.build()
    }
}

impl<T0, T1, T2, T3, T4> FromOcamlRep for (T0, T1, T2, T3, T4)
where
    T0: FromOcamlRep,
    T1: FromOcamlRep,
    T2: FromOcamlRep,
    T3: FromOcamlRep,
    T4: FromOcamlRep,
{
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        let block = from::expect_tuple(value, 5)?;
        let f0: T0 = from::field(block, 0)?;
        let f1: T1 = from::field(block, 1)?;
        let f2: T2 = from::field(block, 2)?;
        let f3: T3 = from::field(block, 3)?;
        let f4: T4 = from::field(block, 4)?;
        Ok((f0, f1, f2, f3, f4))
    }
}

impl<'a, T0, T1, T2, T3, T4> FromOcamlRepIn<'a> for (T0, T1, T2, T3, T4)
where
    T0: FromOcamlRepIn<'a>,
    T1: FromOcamlRepIn<'a>,
    T2: FromOcamlRepIn<'a>,
    T3: FromOcamlRepIn<'a>,
    T4: FromOcamlRepIn<'a>,
{
    fn from_ocamlrep_in<'b>(value: Value<'b>, alloc: &'a Bump) -> Result<Self, FromError> {
        let block = from::expect_tuple(value, 5)?;
        let f0: T0 = from::field_in(block, 0, alloc)?;
        let f1: T1 = from::field_in(block, 1, alloc)?;
        let f2: T2 = from::field_in(block, 2, alloc)?;
        let f3: T3 = from::field_in(block, 3, alloc)?;
        let f4: T4 = from::field_in(block, 4, alloc)?;
        Ok((f0, f1, f2, f3, f4))
    }
}

impl<T0, T1, T2, T3, T4, T5> ToOcamlRep for (T0, T1, T2, T3, T4, T5)
where
    T0: ToOcamlRep,
    T1: ToOcamlRep,
    T2: ToOcamlRep,
    T3: ToOcamlRep,
    T4: ToOcamlRep,
    T5: ToOcamlRep,
{
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        let mut block = alloc.block_with_size(6);
        alloc.set_field(&mut block, 0, alloc.add(&self.0));
        alloc.set_field(&mut block, 1, alloc.add(&self.1));
        alloc.set_field(&mut block, 2, alloc.add(&self.2));
        alloc.set_field(&mut block, 3, alloc.add(&self.3));
        alloc.set_field(&mut block, 4, alloc.add(&self.4));
        alloc.set_field(&mut block, 5, alloc.add(&self.5));
        block.build()
    }
}

impl<T0, T1, T2, T3, T4, T5> FromOcamlRep for (T0, T1, T2, T3, T4, T5)
where
    T0: FromOcamlRep,
    T1: FromOcamlRep,
    T2: FromOcamlRep,
    T3: FromOcamlRep,
    T4: FromOcamlRep,
    T5: FromOcamlRep,
{
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        let block = from::expect_tuple(value, 6)?;
        let f0: T0 = from::field(block, 0)?;
        let f1: T1 = from::field(block, 1)?;
        let f2: T2 = from::field(block, 2)?;
        let f3: T3 = from::field(block, 3)?;
        let f4: T4 = from::field(block, 4)?;
        let f5: T5 = from::field(block, 5)?;
        Ok((f0, f1, f2, f3, f4, f5))
    }
}

impl<'a, T0, T1, T2, T3, T4, T5> FromOcamlRepIn<'a> for (T0, T1, T2, T3, T4, T5)
where
    T0: FromOcamlRepIn<'a>,
    T1: FromOcamlRepIn<'a>,
    T2: FromOcamlRepIn<'a>,
    T3: FromOcamlRepIn<'a>,
    T4: FromOcamlRepIn<'a>,
    T5: FromOcamlRepIn<'a>,
{
    fn from_ocamlrep_in<'b>(value: Value<'b>, alloc: &'a Bump) -> Result<Self, FromError> {
        let block = from::expect_tuple(value, 6)?;
        let f0: T0 = from::field_in(block, 0, alloc)?;
        let f1: T1 = from::field_in(block, 1, alloc)?;
        let f2: T2 = from::field_in(block, 2, alloc)?;
        let f3: T3 = from::field_in(block, 3, alloc)?;
        let f4: T4 = from::field_in(block, 4, alloc)?;
        let f5: T5 = from::field_in(block, 5, alloc)?;
        Ok((f0, f1, f2, f3, f4, f5))
    }
}

impl<T0, T1, T2, T3, T4, T5, T6> ToOcamlRep for (T0, T1, T2, T3, T4, T5, T6)
where
    T0: ToOcamlRep,
    T1: ToOcamlRep,
    T2: ToOcamlRep,
    T3: ToOcamlRep,
    T4: ToOcamlRep,
    T5: ToOcamlRep,
    T6: ToOcamlRep,
{
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        let mut block = alloc.block_with_size(7);
        alloc.set_field(&mut block, 0, alloc.add(&self.0));
        alloc.set_field(&mut block, 1, alloc.add(&self.1));
        alloc.set_field(&mut block, 2, alloc.add(&self.2));
        alloc.set_field(&mut block, 3, alloc.add(&self.3));
        alloc.set_field(&mut block, 4, alloc.add(&self.4));
        alloc.set_field(&mut block, 5, alloc.add(&self.5));
        alloc.set_field(&mut block, 6, alloc.add(&self.6));
        block.build()
    }
}

impl<T0, T1, T2, T3, T4, T5, T6> FromOcamlRep for (T0, T1, T2, T3, T4, T5, T6)
where
    T0: FromOcamlRep,
    T1: FromOcamlRep,
    T2: FromOcamlRep,
    T3: FromOcamlRep,
    T4: FromOcamlRep,
    T5: FromOcamlRep,
    T6: FromOcamlRep,
{
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        let block = from::expect_tuple(value, 7)?;
        let f0: T0 = from::field(block, 0)?;
        let f1: T1 = from::field(block, 1)?;
        let f2: T2 = from::field(block, 2)?;
        let f3: T3 = from::field(block, 3)?;
        let f4: T4 = from::field(block, 4)?;
        let f5: T5 = from::field(block, 5)?;
        let f6: T6 = from::field(block, 6)?;
        Ok((f0, f1, f2, f3, f4, f5, f6))
    }
}

impl<'a, T0, T1, T2, T3, T4, T5, T6> FromOcamlRepIn<'a> for (T0, T1, T2, T3, T4, T5, T6)
where
    T0: FromOcamlRepIn<'a>,
    T1: FromOcamlRepIn<'a>,
    T2: FromOcamlRepIn<'a>,
    T3: FromOcamlRepIn<'a>,
    T4: FromOcamlRepIn<'a>,
    T5: FromOcamlRepIn<'a>,
    T6: FromOcamlRepIn<'a>,
{
    fn from_ocamlrep_in<'b>(value: Value<'b>, alloc: &'a Bump) -> Result<Self, FromError> {
        let block = from::expect_tuple(value, 7)?;
        let f0: T0 = from::field_in(block, 0, alloc)?;
        let f1: T1 = from::field_in(block, 1, alloc)?;
        let f2: T2 = from::field_in(block, 2, alloc)?;
        let f3: T3 = from::field_in(block, 3, alloc)?;
        let f4: T4 = from::field_in(block, 4, alloc)?;
        let f5: T5 = from::field_in(block, 5, alloc)?;
        let f6: T6 = from::field_in(block, 6, alloc)?;
        Ok((f0, f1, f2, f3, f4, f5, f6))
    }
}

impl<T0, T1, T2, T3, T4, T5, T6, T7> ToOcamlRep for (T0, T1, T2, T3, T4, T5, T6, T7)
where
    T0: ToOcamlRep,
    T1: ToOcamlRep,
    T2: ToOcamlRep,
    T3: ToOcamlRep,
    T4: ToOcamlRep,
    T5: ToOcamlRep,
    T6: ToOcamlRep,
    T7: ToOcamlRep,
{
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        let mut block = alloc.block_with_size(8);
        alloc.set_field(&mut block, 0, alloc.add(&self.0));
        alloc.set_field(&mut block, 1, alloc.add(&self.1));
        alloc.set_field(&mut block, 2, alloc.add(&self.2));
        alloc.set_field(&mut block, 3, alloc.add(&self.3));
        alloc.set_field(&mut block, 4, alloc.add(&self.4));
        alloc.set_field(&mut block, 5, alloc.add(&self.5));
        alloc.set_field(&mut block, 6, alloc.add(&self.6));
        alloc.set_field(&mut block, 7, alloc.add(&self.7));
        block.build()
    }
}

impl<T0, T1, T2, T3, T4, T5, T6, T7> FromOcamlRep for (T0, T1, T2, T3, T4, T5, T6, T7)
where
    T0: FromOcamlRep,
    T1: FromOcamlRep,
    T2: FromOcamlRep,
    T3: FromOcamlRep,
    T4: FromOcamlRep,
    T5: FromOcamlRep,
    T6: FromOcamlRep,
    T7: FromOcamlRep,
{
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        let block = from::expect_tuple(value, 8)?;
        let f0: T0 = from::field(block, 0)?;
        let f1: T1 = from::field(block, 1)?;
        let f2: T2 = from::field(block, 2)?;
        let f3: T3 = from::field(block, 3)?;
        let f4: T4 = from::field(block, 4)?;
        let f5: T5 = from::field(block, 5)?;
        let f6: T6 = from::field(block, 6)?;
        let f7: T7 = from::field(block, 7)?;
        Ok((f0, f1, f2, f3, f4, f5, f6, f7))
    }
}

impl<'a, T0, T1, T2, T3, T4, T5, T6, T7> FromOcamlRepIn<'a> for (T0, T1, T2, T3, T4, T5, T6, T7)
where
    T0: FromOcamlRepIn<'a>,
    T1: FromOcamlRepIn<'a>,
    T2: FromOcamlRepIn<'a>,
    T3: FromOcamlRepIn<'a>,
    T4: FromOcamlRepIn<'a>,
    T5: FromOcamlRepIn<'a>,
    T6: FromOcamlRepIn<'a>,
    T7: FromOcamlRepIn<'a>,
{
    fn from_ocamlrep_in<'b>(value: Value<'b>, alloc: &'a Bump) -> Result<Self, FromError> {
        let block = from::expect_tuple(value, 8)?;
        let f0: T0 = from::field_in(block, 0, alloc)?;
        let f1: T1 = from::field_in(block, 1, alloc)?;
        let f2: T2 = from::field_in(block, 2, alloc)?;
        let f3: T3 = from::field_in(block, 3, alloc)?;
        let f4: T4 = from::field_in(block, 4, alloc)?;
        let f5: T5 = from::field_in(block, 5, alloc)?;
        let f6: T6 = from::field_in(block, 6, alloc)?;
        let f7: T7 = from::field_in(block, 7, alloc)?;
        Ok((f0, f1, f2, f3, f4, f5, f6, f7))
    }
}


// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.

/*!
OcamlRep is a framework for building and interpreting the in-memory
representation of OCaml values. This is useful for converting Rust values to
OCaml values and vice-versa, and for building and storing OCaml values off of
the OCaml runtime's garbage-collected heap.

OcamlRep provides a generic interface abstracting over the allocation of OCaml
values, allowing custom allocators to choose where values are allocated
(including directly onto the OCaml heap).

# Example: build an OCaml value ###############################################

This crate provides an arena-allocator which manages the memory in which
converted OCaml values are stored. When the arena is dropped, the values are
freed.

```rust
// Import the Allocator trait for access to its `add` method, which builds an
// OCaml-value representation of the argument (using `OcamlRep::to_ocamlrep`)
// and returns that value.
use ocamlrep::{Allocator, Arena, Value};

// The `ocamlrep` crate provides implementations of `OcamlRep` for builtin types
// like `Option`, `String`, integers, and tuples. This allows them to be
// converted to OCaml values using the Allocator trait (which Arena implements).
let tuple = (Some(42), String::from("a"));

// Allocate a chunk of Rust-managed backing storage for our OCaml value.
let arena = Arena::new();

// This value borrows the Arena, to ensure that we cannot continue to use it
// after the Arena has been freed. OcamlRep values do not borrow the Rust values
// they were converted from--the string "a" is copied into the Arena.
let ocamlrep_value: Value<'_> = arena.add(&tuple);

// We must now convert the value to a usize which can be handed over to the
// OCaml runtime. We must take care when doing this to ensure that our OCaml
// program doesn't use the value after the Arena is freed.
let ocaml_value: usize = ocamlrep_value.to_bits();
```

# Example: return an OCaml value to the OCaml runtime #########################

The value `ocaml_value` from the previous example is suitable to be handed to
the OCaml runtime. We might do so with an extern declaration like this:

```ocaml
external get_tuple : unit -> int option * string = "get_tuple"

let use_rust_tuple () =
  match get_tuple () with
  | (Some i, s) -> Printf.printf "%d, %s\n" i s
  | (None, s) -> Printf.printf "None, %s\n" s
```

We could provide this symbol from the Rust side like this:

```rust
#[no_mangle]
pub extern "C" fn get_tuple(_unit: usize) -> usize {
    use ocamlrep::{Allocator, Arena};
    let arena = Box::leak(Box::new(Arena::new()));
    let ocaml_tuple = arena.add(&(Some(42), String::from("a")));
    // This is safe because we leaked the Arena--no matter what we do with this
    // value on the OCaml side, we'll never use-after-free.
    ocaml_tuple.to_bits()
}
```

But this leaks memory. For small amounts of memory, or a short-lived process,
this might be fine. If not, we might choose another strategy...

# Example: lend an OCaml value to the OCaml runtime ###########################

Instead, we could register an OCaml callback which uses the value:

```ocaml
let use_tuple (tuple : int option * string) : unit =
  match tuple with
  | (Some i, s) -> Printf.printf "%d, %s\n" i s
  | (None, s) -> Printf.printf "None, %s\n" s

external make_and_use_tuple : unit -> unit = "make_and_use_tuple"

let () =
  Callback.register "use_tuple" use_tuple;
  make_and_use_tuple ()
```

And call into OCaml from Rust (using the `ocaml` crate) to hand over the value:

```rust
#[no_mangle]
pub extern "C" fn make_and_use_tuple(_unit: usize) -> usize {
    use ocamlrep::{Allocator, Arena};
    let arena = Arena::new();
    let tuple_ocamlrep = arena.add(&(Some(42), String::from("a")));

    let use_tuple = ocaml::named_value("use_tuple")
        .expect("use_tuple must be registered using Callback.register");

    // This is safe because we are passing the value to `use_tuple`, which
    // doesn't store the value and returns before we free the Arena.
    let ocaml_tuple: usize = tuple_ocamlrep.to_bits();
    use_tuple
        .call(ocaml::Value::new(ocaml_tuple))
        .expect("use_tuple must be a function");

    // Free the arena and return unit.
    ocaml::core::mlvalues::UNIT
}
```

# Example: pass an OCaml value to the OCaml runtime ###########################

The `ocamlrep_ocamlpool` crate provides an `Allocator` which builds values on
the OCaml runtime's garbage-collected heap. We can replace the memory-leaking
implementation from the [second example ("return an OCaml value to the OCaml
runtime")](#example-return-an-ocaml-value-to-the-ocaml-runtime) with one that
allows the OCaml value to be garbage-collected when no longer used:

```rust
#[no_mangle]
pub extern "C" fn get_tuple(_unit: usize) -> usize {
    ocamlrep_ocamlpool::to_ocaml(&(Some(42), String::from("a")))
}
```

The `ocamlrep_ocamlpool` crate provides a convenience macro for this use case:

```rust
use ocamlrep_ocamlpool::ocaml_ffi;

ocaml_ffi! {
    // This expands to code similar to the above definition of get_tuple.
    fn get_tuple() -> (Option<i32>, String) {
        (Some(42), String::from("a"))
    }
}
```

# Example: pass an OCaml value to Rust ########################################

An `Allocator` converts Rust values which implement the `OcamlRep` trait into
OCaml values using the method `OcamlRep::to_ocamlrep`. The `OcamlRep` trait also
provides a method `OcamlRep::from_ocamlrep` (and an FFI helper named
`OcamlRep::from_ocaml`) for conversion in the other direction.

If we call into Rust like this:

```ocaml
external use_tuple : int option * string -> unit = "use_tuple"

let () = use_tuple (Some 42, "a")
```

We could convert the tuple to a Rust value like this:

```rust
#[no_mangle]
pub extern "C" fn use_tuple(ocaml_tuple: usize) -> usize {
    // Import the OcamlRep trait to use its associated function `from_ocaml`.
    use ocamlrep::OcamlRep;
    // Safety: `ocaml_tuple` is a valid OCaml value allocated by the OCaml
    // runtime, all objects reachable from that value are also valid OCaml
    // values, and those objects cannot be concurrently modified while
    // `from_ocaml` runs. This is true because that graph of objects is owned by
    // the OCaml runtime, we didn't expose any of their pointers in any other
    // FFI functions, and the OCaml runtime is both single-threaded and
    // currently interrupted by an FFI call into this function.
    let tuple_result = unsafe { <(Option<i32>, String)>::from_ocaml(ocaml_tuple) };
    let tuple = tuple_result
        .expect("Expected a value of type `int option * string`, \
                 but got some other type or invalid UTF-8");
    println!("{:?}", tuple);
    ocaml::core::mlvalues::UNIT
}
```

The `ocaml_ffi!` macro in the `ocamlrep_ocamlpool` crate supports this use case:

```rust
use ocamlrep_ocamlpool::ocaml_ffi;

ocaml_ffi! {
    // This expands to code similar to the above definition of use_tuple.
    fn use_tuple(tuple: (Option<i32>, String)) {
        println!("{:?}", tuple)
    }
}
```

Note that the value returned by `from_ocaml` is owned--it is effectively a
deep clone of the OCaml value. For instance, the OCaml string "a" is copied into
a newly allocated Rust String--the Rust side does not need to worry about the
OCaml value being garbage collected.

Take care when using `from_ocaml`, `from_ocamlrep`, or `ocaml_ffi!` with types
containing `String`s. OCaml strings are not guaranteed to be UTF-8, so
`from_ocaml` may return an `Err` because a string was invalid UTF-8 rather than
because the OCaml code did not pass a value of the expected type (which should
be forbidden by the OCaml compiler, provided that the `external` declaration is
annotated with the correct type). If representing invalid UTF-8 is a
requirement, use `Vec<u8>` instead (an implementation of `OcamlRep` which
converts `Vec<u8>` to an OCaml `string` is provided).

# Example: implementing OcamlRep ##############################################

Writing a manual implementation of OcamlRep requires one to choose some type
with which their value should be represented in OCaml and write `to_ocamlrep`
and `from_ocamlrep` conversion functions which build and interpret the in-memory
representation of that type.

This crate provides implementations of OcamlRep for several std types, and
chooses these OCaml types to represent them with:

| Rust type              | OCaml type                              |
|------------------------|-----------------------------------------|
| `()`                   | `unit`                                  |
| `bool`                 | `bool`                                  |
| `usize`/`isize`        | `int`                                   |
| [`Option`][OptionRs]   | [`option`][OptionMl]                    |
| [`Result`][ResultRs]   | [`result`][ResultMl]                    |
| [`Vec`][Vec]           | [`list`][List]                          |
| [`String`][StringRs]   | [`string`][StringMl] (when valid UTF-8) |
| [`Vec<u8>`][Vec]       | [`string`][StringMl]                    |
| [`PathBuf`][PathBuf]   | [`string`][StringMl]                    |
| [`BTreeMap`][BTreeMap] | [`Map`][Map]                            |
| [`BTreeSet`][BTreeSet] | [`Set`][Set]                            |

[OptionRs]: https://doc.rust-lang.org/beta/std/option/enum.Option.html
[OptionMl]: https://caml.inria.fr/pub/docs/manual-ocaml/libref/Option.html
[ResultRs]: https://doc.rust-lang.org/std/result/enum.Result.html
[ResultMl]: https://caml.inria.fr/pub/docs/manual-ocaml/libref/Result.html
[Vec]: https://doc.rust-lang.org/std/vec/struct.Vec.html
[List]: https://caml.inria.fr/pub/docs/manual-ocaml/libref/List.html
[StringRs]: https://doc.rust-lang.org/std/string/struct.String.html
[StringMl]: https://caml.inria.fr/pub/docs/manual-ocaml/libref/String.html
[PathBuf]: https://doc.rust-lang.org/std/path/struct.PathBuf.html
[BTreeMap]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html
[Map]: https://caml.inria.fr/pub/docs/manual-ocaml/libref/Map.html
[BTreeSet]: https://doc.rust-lang.org/std/collections/struct.BTreeSet.html
[Set]: https://caml.inria.fr/pub/docs/manual-ocaml/libref/Set.html

See the [`impls`](../src/ocamlrep/impls.rs.html) submodule for examples of
conversions for std types, and [Real World
OCaml](https://dev.realworldocaml.org/runtime-memory-layout.html) and the
[Interfacing C with OCaml](https://ocaml.org/manual/intfc.html) section of the
OCaml manual for description of the OCaml representation of values.

Since manually implementing OcamlRep is cumbersome and error-prone, the
`ocamlrep_derive` crate provides a procedural macro for deriving the OcamlRep
trait.

When derived for custom types like this:

```ocaml
use ocamlrep_derive::OcamlRep;

#[derive(OcamlRep)]
struct Foo {
    a: isize,
    b: Vec<bool>,
}

#[derive(OcamlRep)]
enum Fruit {
    Apple,
    Orange(isize),
    Pear { num: isize },
    Kiwi,
}
```

It produces implementations of OcamlRep which construct values belonging to
these roughly-equivalent OCaml types:

```ocaml
type foo = {
  a: int;
  b: bool list;
}

type fruit =
  | Apple
  | Orange of int
  | Pear of { num: int }
  | Kiwi
```

The `oxidize` program at hphp/hack/src/hh_oxidize will take OCaml source files
like the one above and generate Rust source files which define
"roughly-equivalent" types (like the Rust source above). These types will derive
an implementation of `OcamlRep` which will produce OCaml values belonging to the
corresponding type in the OCaml source file.
*/

mod arena;
mod block;
mod cache;
mod error;
mod impls;
mod value;

pub mod from;
pub mod ptr;
pub mod rc;

pub use arena::Arena;
pub use block::Block;
pub use block::BlockBuilder;
pub use block::BlockBytes;
pub use block::Color;
pub use block::Header;
pub use block::ABSTRACT_TAG;
pub use block::CLOSURE_TAG;
pub use block::CONT_TAG;
pub use block::CUSTOM_TAG;
pub use block::DOUBLE_ARRAY_TAG;
pub use block::DOUBLE_TAG;
pub use block::FORCING_TAG;
pub use block::FORWARD_TAG;
pub use block::INFIX_TAG;
pub use block::LAZY_TAG;
pub use block::NO_SCAN_TAG;
pub use block::OBJECT_TAG;
pub use block::STRING_TAG;
pub use bumpalo::Bump;
pub use cache::MemoizationCache;
pub use error::FromError;
pub use impls::bytes_from_ocamlrep;
pub use impls::bytes_to_ocamlrep;
pub use impls::sorted_iter_to_ocaml_map;
pub use impls::sorted_iter_to_ocaml_set;
pub use impls::str_from_ocamlrep;
pub use impls::str_to_ocamlrep;
pub use impls::vec_from_ocaml_map;
pub use impls::vec_from_ocaml_map_in;
pub use impls::vec_from_ocaml_set;
pub use impls::vec_from_ocaml_set_in;
pub use ocamlrep_derive::FromOcamlRep;
pub use ocamlrep_derive::FromOcamlRepIn;
pub use ocamlrep_derive::ToOcamlRep;
pub use value::Value;

// 'mlvalues.h'
pub const DOUBLE_WOSIZE: usize = std::mem::size_of::<f64>() / std::mem::size_of::<usize>();

// 'gc.h'
pub const CAML_WHITE: usize = 0 << 8;
pub const CAML_GRAY: usize = 1 << 8;
pub const CAML_BLUE: usize = 2 << 8;
pub const CAML_BLACK: usize = 3 << 8;

/// A data structure that can be converted to an OCaml value.
///
/// Types which implement both `ToOcamlRep` and `FromOcamlRep` (or
/// `FromOcamlRepIn`) should provide compatible implementations thereof.
/// In other words, it is expected that for any value with type `T`,
/// `T::from_ocamlrep(value.to_ocamlrep(alloc)) == Ok(value)`.
pub trait ToOcamlRep {
    /// Allocate an OCaml representation of `self` using the given Allocator.
    ///
    /// Implementors of this method must not mutate or drop any values after
    /// passing them to `Allocator::add` (or invoking `to_ocamlrep` on them),
    /// else `Allocator::memoized` may return incorrect results (this can
    /// generally only be done using internal-mutability types like `RefCell`,
    /// `Mutex`, or atomics, or by using `unsafe`).
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a>;
}

/// An interface for allocating OCaml values in some allocator-defined memory region.
pub trait Allocator: Sized {
    /// Return a token which uniquely identifies this Allocator. The token must
    /// be unique (among all instances of all implementors of the Allocator
    /// trait), and an instance of an implementor of Allocator must return the
    /// same token throughout its entire lifetime.
    fn generation(&self) -> usize;

    /// Allocate a block with enough space for `size` fields, write its header,
    /// and return it.
    fn block_with_size_and_tag(&self, size: usize, tag: u8) -> BlockBuilder<'_>;

    /// Write the given value to the `index`th field of `block`.
    ///
    /// # Panics
    ///
    /// Panics if `index` is out of bounds for `block` (i.e., greater than or
    /// equal to the block's size).
    fn set_field<'a>(&self, block: &mut BlockBuilder<'a>, index: usize, value: Value<'a>);

    /// # Safety
    ///
    /// Must be used only with values allocated by this `Allocator`. The caller
    /// may assume the returned pointer is valid only until some other
    /// `Allocator` method is called on `self` (since an allocation may
    /// invalidate the pointed-to memory).
    ///
    /// Intended to be used only in implementations of `Allocator::set_field`
    /// and in conversion-to-OCaml functions requiring access to the raw memory
    /// of a block (e.g., `bytes_to_ocamlrep`).
    unsafe fn block_ptr_mut<'a>(&self, block: &mut BlockBuilder<'a>) -> *mut Value<'a>;

    #[inline(always)]
    fn block_with_size(&self, size: usize) -> BlockBuilder<'_> {
        self.block_with_size_and_tag(size, 0u8)
    }

    /// Convert the given data structure to an OCaml value. Structural sharing
    /// (via references or `Rc`) will not be preserved unless `add` is invoked
    /// within an outer invocation of `add_root`.
    ///
    /// To preserve structural sharing without using `add_root` (and the
    /// overhead of maintaining a cache that comes with it), consider using
    /// `ocamlrep::rc::RcOc` instead of `Rc`.
    #[inline(always)]
    fn add<'a, T: ToOcamlRep + ?Sized>(&'a self, value: &'a T) -> Value<'a> {
        value.to_ocamlrep(self)
    }

    /// Convert the given `Copy` data structure to an OCaml value.
    #[inline(always)]
    fn add_copy<'a, T: ToOcamlRep + Copy + 'static>(&'a self, value: T) -> Value<'a> {
        let value_ref = &value;
        // SAFETY: add/to_ocamlrep cannot reference `value` in the `Value`
        // they return, and the `Copy + 'static` bounds ensure that we're not
        // working with a reference or Rc which might be invalidated.
        self.add(unsafe { std::mem::transmute::<&'_ T, &'a T>(value_ref) })
    }

    /// Given the address and size of some value, and a function to convert the
    /// value to OCaml (e.g., a closure `|alloc| (*slice).to_ocamlrep(alloc)`),
    /// either execute the function and return its result, or return a cached
    /// result for that address.
    ///
    /// If `memoized` is invoked without an outer invocation of `add_root`, it
    /// must never return a cached result. If `memoized` is invoked within an
    /// outer invocation of `add_root`, it must return a result computed within
    /// that invocation of `add_root`.
    fn memoized<'a>(
        &'a self,
        ptr: usize,
        size_in_bytes: usize,
        f: impl FnOnce(&'a Self) -> Value<'a>,
    ) -> Value<'a>;

    /// Convert the given data structure to an OCaml value. Structural sharing
    /// (via references or `Rc`) will be preserved.
    ///
    /// Note that sharing is preserved using a memoization cache keyed off of
    /// address and size only. If the given value contains multiple references
    /// or slices pointing to equal-sized views of the same data, but with
    /// different OCaml representations, e.g.:
    ///
    /// ```
    /// let x: &(u32, u32) = &(0u32, 1u32);
    /// let y: &u64 = unsafe { std::mem::transmute(x) };
    /// let value = (x, y);
    /// alloc.add_root(&value) // PROBLEM!
    /// ```
    ///
    /// Then the converted OCaml value will not have the intended type (i.e.,
    /// the one which would be produced by `Allocator::add`). In this example,
    /// the allocator will memoize the result of converting `x` (an OCaml
    /// tuple), and use it for both `x` and `y` in `value` (since they point to
    /// equal-sized types at the same address).
    ///
    /// # Panics
    ///
    /// `add_root` is not re-entrant, and panics upon attempts to do so.
    fn add_root<'a, T: ToOcamlRep + ?Sized>(&'a self, value: &'a T) -> Value<'a>;

    /// Allocate a block with tag `STRING_TAG` and enough space for a string of
    /// `len` bytes. Write its header and return a `BlockBytes` wrapping the
    /// buffer and the block.
    fn byte_string_with_len<'a>(&'a self, len: usize) -> BlockBytes<'a> {
        let word_size = std::mem::size_of::<*const u8>();
        let words = (len + 1 /*null-ending*/ + (word_size - 1)/*rounding*/) / word_size;
        let length = words * word_size;
        let mut block = self.block_with_size_and_tag(words, STRING_TAG);
        unsafe {
            let block = self.block_ptr_mut(&mut block);
            *block.add(words - 1) = Value::from_bits(0);
            let block_bytes = block as *mut u8;
            *block_bytes.add(length - 1) = (length - len - 1) as u8;
            BlockBytes::new(std::slice::from_raw_parts_mut(block_bytes, len))
        }
    }
}

/// A type which can be reconstructed from an OCaml value.
///
/// Types which implement both `ToOcamlRep` and `FromOcamlRep` should provide
/// compatible implementations thereof. In other words, it is expected that for
/// any value, `T::from_ocamlrep(value.to_ocamlrep(alloc)) == Ok(value)`.
pub trait FromOcamlRep: Sized {
    /// Convert the given ocamlrep Value to a value of type `Self`, if possible.
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError>;

    /// Convert the given OCaml value to a value of type `Self`, if possible.
    ///
    /// # Safety
    ///
    /// The given value must be a valid OCaml value. All values reachable from
    /// the given value must be valid OCaml values. None of these values may be
    /// naked pointers. None of these values may be modified while `from_ocaml`
    /// is running.
    unsafe fn from_ocaml(value: usize) -> Result<Self, FromError> {
        Self::from_ocamlrep(Value::from_bits(value))
    }
}

/// A type which can be reconstructed from an OCaml value.
///
/// Types which implement both `ToOcamlRep` and `FromOcamlRepIn` should provide
/// compatible implementations thereof. In other words, it is expected that for
/// any value, `T::from_ocamlrep_in(value.to_ocamlrep(alloc), bump) == Ok(value)`.
pub trait FromOcamlRepIn<'a>: Sized {
    /// Convert the given ocamlrep Value to a value of type `Self`, allocated in
    /// the given arena.
    fn from_ocamlrep_in(value: Value<'_>, arena: &'a Bump) -> Result<Self, FromError>;
}


// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.

//! FFI types for representing pointers-to-OCaml-managed-data in Rust
//! (`UnsafeOcamlPtr`) and pointers-to-Rust-managed-data in OCaml (`NakedPtr`).

use std::fmt;
use std::num::NonZeroUsize;

use crate::Allocator;
use crate::FromError;
use crate::FromOcamlRep;
use crate::ToOcamlRep;
use crate::Value;

/// Unsafe pointer to an OCaml value which is (possibly) managed by the garbage
/// collector.
///
/// Take care that the value stays rooted or the garbage collector does not run
/// while an UnsafeOcamlPtr wrapper for it exists.
///
/// While this can be used with an ocamlrep::Arena via to_ocamlrep, caution is
/// required--the pointed-to value will *not* be cloned into the Arena, so a
/// data structure containing UnsafeOcamlPtrs which is allocated into an Arena
/// may contain pointers into the OCaml GC-ed heap.
#[repr(transparent)]
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub struct UnsafeOcamlPtr(NonZeroUsize);

impl UnsafeOcamlPtr {
    pub unsafe fn new(ptr: usize) -> Self {
        Self(NonZeroUsize::new(ptr).unwrap())
    }

    pub fn as_usize(self) -> usize {
        self.0.get()
    }

    #[inline(always)]
    pub const fn is_int(self) -> bool {
        // SAFETY: `Value::is_int` only checks the low bit, so it's safe
        // to interpret `self.0` as a value (we don't attempt to dereference it)
        unsafe { self.as_value().is_int() }
    }

    #[inline(always)]
    pub const fn is_block(self) -> bool {
        // SAFETY: `Value::is_block` only checks the low bit, so it's safe
        // to interpret `self.0` as a value (we don't attempt to dereference it)
        unsafe { self.as_value().is_block() }
    }

    /// Interpret this pointer as an OCaml value which is valid for lifetime 'a.
    /// The OCaml garbage collector must not run during this lifetime (even if
    /// the value is rooted).
    #[inline(always)]
    pub const unsafe fn as_value<'a>(self) -> Value<'a> {
        Value::from_bits(self.0.get())
    }
}

impl fmt::Debug for UnsafeOcamlPtr {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "0x{:x}", self.0)
    }
}

impl ToOcamlRep for UnsafeOcamlPtr {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, _alloc: &'a A) -> Value<'a> {
        unsafe { Value::from_bits(self.0.get()) }
    }
}

impl FromOcamlRep for UnsafeOcamlPtr {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        if value.is_int() {
            return Err(FromError::ExpectedBlock(value.as_int().unwrap()));
        }
        Ok(unsafe { Self::new(value.to_bits()) })
    }
}

impl<'a> crate::FromOcamlRepIn<'a> for UnsafeOcamlPtr {
    fn from_ocamlrep_in(value: Value<'_>, _alloc: &'a bumpalo::Bump) -> Result<Self, FromError> {
        Self::from_ocamlrep(value)
    }
}

/// Any kind of foreign pointer (i.e., a pointer to any data at all--it need not
/// look like a valid OCaml value).
///
/// On the OCaml side, these are represented as opaque types, e.g. `type addr;`.
///
/// The pointer must not be within a memory page currently in use by the OCaml
/// runtime for the garbage-collected heap (i.e., it must in fact be a foreign
/// pointer).
///
/// Can only be used when linking against a binary built with an OCaml compiler
/// which was **not** configured with the `-no-naked-pointers` option (which
/// forbids naked pointers, requiring foreign pointers to be wrapped in a block
/// tagged with `Abstract_tag` instead).
#[repr(transparent)]
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub struct NakedPtr<T>(*const T);

impl<T> NakedPtr<T> {
    pub fn new(ptr: *const T) -> Self {
        Self(ptr)
    }

    pub fn as_ptr(self) -> *const T {
        self.0
    }
}

impl<T> fmt::Debug for NakedPtr<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:p}", self.0)
    }
}

impl<T> ToOcamlRep for NakedPtr<T> {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, _alloc: &'a A) -> Value<'a> {
        unsafe { Value::from_bits(self.0 as usize) }
    }
}

impl<T> FromOcamlRep for NakedPtr<T> {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        if value.is_int() {
            return Err(FromError::ExpectedBlock(value.as_int().unwrap()));
        }
        Ok(Self::new(value.to_bits() as *const T))
    }
}


// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.

//! Provides `RcOc`, a single-threaded reference-counting pointer. `RcOc` stands
//! for "reference counted with Ocaml-value cache".

use std::cell::Cell;
use std::cmp::Ordering;
use std::fmt;
use std::hash::Hash;
use std::hash::Hasher;
use std::ops::Deref;
use std::rc::Rc;

use bumpalo::Bump;
use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;
use serde::Serializer;

use crate::Allocator;
use crate::FromError;
use crate::FromOcamlRep;
use crate::ToOcamlRep;
use crate::Value;

const UNIT: usize = crate::value::isize_to_ocaml_int(0);
const INVALID_GENERATION: usize = usize::max_value();

struct OcamlValueCache<T> {
    forward_pointer: Cell<usize>,
    generation: Cell<usize>,
    value: T,
}

impl<T> OcamlValueCache<T> {
    #[inline(always)]
    pub fn new(value: T) -> Self {
        OcamlValueCache {
            forward_pointer: Cell::new(UNIT),
            generation: Cell::new(INVALID_GENERATION),
            value,
        }
    }

    #[inline(always)]
    pub fn get_in_generation(&self, generation: usize) -> Option<usize> {
        if generation == self.generation.get() {
            Some(self.forward_pointer.get())
        } else {
            None
        }
    }

    #[inline(always)]
    pub fn set(&self, value: usize, generation: usize) {
        assert!(generation != INVALID_GENERATION);
        self.forward_pointer.set(value);
        self.generation.set(generation);
    }

    #[inline(always)]
    fn clear(&self) {
        self.forward_pointer.set(UNIT);
        self.generation.set(INVALID_GENERATION);
    }
}

impl<T: Clone> Clone for OcamlValueCache<T> {
    #[inline(always)]
    fn clone(&self) -> Self {
        OcamlValueCache::new(self.value.clone())
    }
}

#[allow(clippy::partialeq_ne_impl)]
impl<T: PartialEq> PartialEq for OcamlValueCache<T> {
    #[inline(always)]
    fn eq(&self, other: &OcamlValueCache<T>) -> bool {
        self.value.eq(&other.value)
    }

    #[inline(always)]
    fn ne(&self, other: &OcamlValueCache<T>) -> bool {
        self.value.ne(&other.value)
    }
}

impl<T: Eq> Eq for OcamlValueCache<T> {}

/// A single-threaded reference-counting pointer type, which, as a performance
/// optimization, can cache the result of converting the pointed-to value to an
/// OCaml value. `RcOc` stands for "reference counted with Ocaml-value cache".
///
/// Internally uses `std::rc::Rc`, so restrictions on `Rc` also apply to `RcOc`.
/// It is encouraged to follow `Rc` conventions (such as preferring the use of
/// `RcOc::clone(x)` rather than `x.clone()`) when using `RcOc`.
pub struct RcOc<T> {
    ptr: Rc<OcamlValueCache<T>>,
}

impl<T> RcOc<T> {
    #[inline(always)]
    pub fn new(value: T) -> Self {
        Self {
            ptr: Rc::new(OcamlValueCache::new(value)),
        }
    }

    #[inline(always)]
    pub fn get_cached_value_in_generation(&self, generation: usize) -> Option<usize> {
        (*self.ptr).get_in_generation(generation)
    }

    #[inline(always)]
    pub fn set_cached_value(&self, value: usize, generation: usize) {
        (*self.ptr).set(value, generation)
    }

    #[inline(always)]
    pub fn clear_cache(&self) {
        (*self.ptr).clear();
    }

    #[inline(always)]
    pub fn get_mut(this: &mut Self) -> Option<&mut T> {
        Rc::get_mut(&mut this.ptr).map(|cache| {
            // We are about to give permission to mutate the value, so
            // invalidate the cache.
            cache.clear();
            &mut cache.value
        })
    }

    #[inline(always)]
    pub fn ptr_eq(this: &Self, other: &Self) -> bool {
        Rc::ptr_eq(&this.ptr, &other.ptr)
    }

    /// Returns the inner value, if the `Rc` has exactly one strong reference.
    ///
    /// Otherwise, an `Err` is returned with the same `Rc` that was passed in.
    ///
    /// This will succeed even if there are outstanding weak references.
    #[inline(always)]
    pub fn try_unwrap(this: Self) -> Result<T, Self> {
        match Rc::try_unwrap(this.ptr) {
            Ok(cache) => Ok(cache.value),
            Err(ptr) => Err(Self { ptr }),
        }
    }
}

impl<T: Clone> RcOc<T> {
    #[inline(always)]
    pub fn make_mut(this: &mut Self) -> &mut T {
        // If the refcount is 1, Rc::make_mut will give permission to mutate the
        // value (rather than cloning it), so invalidate the cache.
        if Rc::strong_count(&this.ptr) == 1 {
            this.clear_cache();
        }
        &mut Rc::make_mut(&mut this.ptr).value
    }
}

impl<T> AsRef<T> for RcOc<T> {
    #[inline(always)]
    fn as_ref(&self) -> &T {
        &self.ptr.as_ref().value
    }
}

impl<T> Clone for RcOc<T> {
    #[inline(always)]
    fn clone(&self) -> Self {
        Self {
            ptr: Rc::clone(&self.ptr),
        }
    }
}

impl<T> Deref for RcOc<T> {
    type Target = T;

    #[inline(always)]
    fn deref(&self) -> &T {
        &self.ptr.deref().value
    }
}

#[allow(clippy::partialeq_ne_impl)]
impl<T: PartialEq> PartialEq for RcOc<T> {
    #[inline(always)]
    fn eq(&self, other: &RcOc<T>) -> bool {
        self.ptr.eq(&other.ptr)
    }

    #[inline(always)]
    fn ne(&self, other: &RcOc<T>) -> bool {
        self.ptr.ne(&other.ptr)
    }
}

impl<T: Eq> Eq for RcOc<T> {}

impl<T: PartialOrd> PartialOrd for RcOc<T> {
    #[inline(always)]
    fn partial_cmp(&self, other: &RcOc<T>) -> Option<Ordering> {
        (**self).partial_cmp(&**other)
    }

    #[inline(always)]
    fn lt(&self, other: &RcOc<T>) -> bool {
        **self < **other
    }

    #[inline(always)]
    fn le(&self, other: &RcOc<T>) -> bool {
        **self <= **other
    }

    #[inline(always)]
    fn gt(&self, other: &RcOc<T>) -> bool {
        **self > **other
    }

    #[inline(always)]
    fn ge(&self, other: &RcOc<T>) -> bool {
        **self >= **other
    }
}

impl<T: Ord> Ord for RcOc<T> {
    #[inline]
    fn cmp(&self, other: &RcOc<T>) -> Ordering {
        (**self).cmp(&**other)
    }
}

impl<T: Hash> Hash for RcOc<T> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        (**self).hash(state);
    }
}

impl<T: fmt::Display> fmt::Display for RcOc<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&**self, f)
    }
}

impl<T: fmt::Debug> fmt::Debug for RcOc<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Debug::fmt(&**self, f)
    }
}

impl<T> fmt::Pointer for RcOc<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Pointer::fmt(&(&**self as *const T), f)
    }
}

impl<T: ToOcamlRep> ToOcamlRep for RcOc<T> {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> {
        let generation = alloc.generation();
        match self.get_cached_value_in_generation(generation) {
            Some(value) => unsafe { Value::from_bits(value) },
            None => {
                let value = alloc.add(self.as_ref());
                self.set_cached_value(value.to_bits(), generation);
                value
            }
        }
    }
}

impl<T: FromOcamlRep> FromOcamlRep for RcOc<T> {
    fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
        // NB: We don't get any sharing this way.
        Ok(RcOc::new(T::from_ocamlrep(value)?))
    }
}

impl<'a, T: FromOcamlRep> crate::FromOcamlRepIn<'a> for RcOc<T> {
    fn from_ocamlrep_in(value: Value<'_>, _alloc: &'a Bump) -> Result<Self, FromError> {
        Self::from_ocamlrep(value)
    }
}

impl<T: Serialize> Serialize for RcOc<T> {
    #[inline]
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        (**self).serialize(serializer)
    }
}

impl<'de, T: Deserialize<'de>> Deserialize<'de> for RcOc<T> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        // NB: We don't get any sharing this way.
        // FWIW, looks like serde doesn't preserve sharing for Rc either.
        // https://github.com/serde-rs/serde/blob/a00aee14950baca7de2e334b895e203b013712da/serde/src/de/impls.rs#L1806-L1808
        Deserialize::deserialize(deserializer).map(RcOc::new)
    }
}


// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.

use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt;
use std::fmt::Debug;
use std::marker::PhantomData;

use crate::block;
use crate::block::Block;
use crate::block::Header;
use crate::Allocator;

#[inline(always)]
pub const fn is_ocaml_int(value: usize) -> bool {
    value & 1 == 1
}

#[inline(always)]
pub const fn isize_to_ocaml_int(value: isize) -> usize {
    ((value as usize) << 1) | 1
}

#[inline(always)]
pub const fn ocaml_int_to_isize(value: usize) -> isize {
    (value as isize) >> 1
}

/// A value, as represented by OCaml. Valid, immutable, and immovable for
/// lifetime `'a`.
///
/// Either a tagged integer value or a pointer to a [`Block`](struct.Block.html)
/// containing fields or binary data.
#[repr(transparent)]
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub struct Value<'a>(pub(crate) usize, PhantomData<&'a ()>);

impl<'a> Value<'a> {
    #[inline(always)]
    pub const fn is_int(self) -> bool {
        is_ocaml_int(self.0)
    }

    #[inline(always)]
    pub const fn is_block(self) -> bool {
        !self.is_int()
    }

    #[inline(always)]
    pub const fn int(value: isize) -> Value<'static> {
        Value(isize_to_ocaml_int(value), PhantomData)
    }

    #[inline(always)]
    pub fn as_int(self) -> Option<isize> {
        if self.is_int() {
            Some(ocaml_int_to_isize(self.0))
        } else {
            None
        }
    }

    #[inline(always)]
    pub fn as_block(self) -> Option<Block<'a>> {
        if self.is_int() {
            return None;
        }
        let block = unsafe {
            let ptr = self.0 as *const Value<'_>;
            let header = ptr.offset(-1);
            let size = Header::from_bits((*header).to_bits()).size() + 1;
            std::slice::from_raw_parts(header, size)
        };
        Some(Block(block))
    }

    #[inline(always)]
    pub fn as_float(self) -> Option<f64> {
        let block = self.as_block()?;
        if block.tag() != block::DOUBLE_TAG {
            return None;
        }
        Some(f64::from_bits(block[0].0 as u64))
    }

    #[inline(always)]
    pub fn as_double_array(self) -> Option<&'a [f64]> {
        let block = self.as_block()?;
        if block.tag() != block::DOUBLE_ARRAY_TAG {
            return None;
        }
        Some(unsafe {
            std::slice::from_raw_parts(block.0.as_ptr().add(1) as *const f64, block.size())
        })
    }

    #[inline(always)]
    pub fn as_byte_string(self) -> Option<&'a [u8]> {
        let block = self.as_block()?;
        if block.tag() != block::STRING_TAG {
            return None;
        }
        let slice = unsafe {
            let size = block.size() * std::mem::size_of::<Value<'_>>();
            let ptr = self.0 as *mut u8;
            let last_byte = ptr.offset(size as isize - 1);
            let padding = *last_byte;
            let size = size - padding as usize - 1;
            std::slice::from_raw_parts(ptr, size)
        };
        Some(slice)
    }

    #[inline(always)]
    pub fn as_str(self) -> Option<Cow<'a, str>> {
        let slice = self.as_byte_string()?;
        Some(String::from_utf8_lossy(slice))
    }

    #[inline(always)]
    pub fn field(self, index: usize) -> Option<Value<'a>> {
        self.field_ref(index).copied()
    }

    #[inline(always)]
    pub fn field_ref(self, index: usize) -> Option<&'a Value<'a>> {
        self.as_block()?.as_values()?.get(index)
    }

    /// Given a pointer to the first field of a [`Block`](struct.Block.html),
    /// create a pointer `Value` referencing that `Block`.
    ///
    /// # Safety
    ///
    /// This method is unsafe because it requires that the pointed-to Value is
    /// the first field of a block, which must be preceded by a valid Header
    /// correctly describing the block's size and tag (i.e., value.offset(-1)
    /// should point to that Header). To be used only with pointers returned by
    /// Arena allocation methods (e.g.,
    /// [`Allocator::block_with_size_and_tag`](trait.Allocator.html#tymethod.block_with_size_and_tag).
    #[inline(always)]
    pub unsafe fn from_ptr(value: *const Value<'a>) -> Value<'a> {
        Value(value as usize, PhantomData)
    }

    /// # Safety
    ///
    /// The lifetime 'a returned is arbitrarily chosen, and does not necessarily
    /// reflect the actual lifetime of the data. If the given value is a
    /// pointer, it is up to the caller to ensure that for the duration of this
    /// lifetime, the block this pointer points to, and every value reachable
    /// from it, does not get written to.
    #[inline(always)]
    pub const unsafe fn from_bits(value: usize) -> Value<'a> {
        Value(value, PhantomData)
    }

    /// Convert this value to a usize, which can be handed to the OCaml runtime
    /// to be used as an OCaml value. Take care that the returned value does
    /// not outlive the arena.
    #[inline(always)]
    pub const fn to_bits(self) -> usize {
        self.0
    }

    /// Helper for `Value::clone_with_allocator`.
    pub(crate) fn clone_with<'b>(
        self,
        alloc: &'b impl Allocator,
        seen: &mut HashMap<usize, Value<'b>>,
    ) -> Value<'b> {
        match self.as_block() {
            None => Value(self.0, PhantomData),
            Some(block) => {
                if let Some(&copied_value) = seen.get(&self.0) {
                    return copied_value;
                }
                let copied_value = block.clone_with(alloc, seen);
                seen.insert(self.0, copied_value);
                copied_value
            }
        }
    }

    /// Recursively clone this `Value` using the given `Allocator`. Structural
    /// sharing is preserved (i.e., values which are physically equal before the
    /// clone will be physically equal after the clone).
    pub fn clone_with_allocator(self, alloc: &impl Allocator) -> Value<'_> {
        self.clone_with(alloc, &mut HashMap::new())
    }
}

impl Debug for Value<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self.as_block() {
            None => write!(f, "{}", self.as_int().unwrap()),
            Some(block) => write!(f, "{:?}", block),
        }
    }
}


// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.

#![cfg(test)]

use ocamlrep::Allocator;
use ocamlrep::Arena;
use ocamlrep::FromOcamlRep;
use ocamlrep::ToOcamlRep;

#[test]
fn shared_str() {
    // Without `add_root`, converting this tuple would convert the string
    // "hello" to its OCaml representation and copy it into the ocamlrep::Arena
    // twice.
    let arena = Arena::new();
    let s = "hello";
    let tuple = (s, s);
    let ocaml_tuple = arena.add_root(&tuple);
    let ocaml_block = ocaml_tuple.as_block().unwrap();

    assert_eq!(
        ocaml_block[0].as_str(),
        Some(std::borrow::Cow::Borrowed("hello"))
    );
    assert_eq!(
        ocaml_block[1].as_str(),
        Some(std::borrow::Cow::Borrowed("hello"))
    );

    // The string pointer in the first field is physically equal to the string
    // pointer in the second field.
    assert_eq!(ocaml_block[0].to_bits(), ocaml_block[1].to_bits());
}

#[test]
fn shared_slice() {
    // Without `add_root`, converting this tuple would convert the list to its
    // OCaml representation and copy it into the ocamlrep::Arena twice.
    let arena = Arena::new();
    let s = &[1usize, 2, 3][..];
    let tuple = (s, s);
    let ocaml_tuple = arena.add_root(&tuple);

    assert_eq!(
        <(Vec<usize>, Vec<usize>)>::from_ocamlrep(ocaml_tuple),
        Ok((vec![1, 2, 3], vec![1, 2, 3]))
    );

    // The list pointer in the first field is physically equal to the list
    // pointer in the second field.
    let ocaml_block = ocaml_tuple.as_block().unwrap();
    assert_eq!(ocaml_block[0].to_bits(), ocaml_block[1].to_bits());
}

#[test]
fn overlapping_substrs() {
    // Without `Allocator::memoized_slice`, a naive implementation of
    // `ToOcamlRep` for slices might use `Allocator::memoized`, failing to take
    // the slice length into consideration.
    // Then we'd incorrectly produce the tuple ("hello", "hello").
    let arena = Arena::new();
    let s1 = "hello";
    let s2 = &s1[..4];
    let tuple = (s1, s2);
    let ocaml_tuple = arena.add_root(&tuple);

    assert_eq!(
        <(String, String)>::from_ocamlrep(ocaml_tuple),
        Ok((String::from("hello"), String::from("hell")))
    );
}

#[test]
fn overlapping_subslices() {
    // Without `Allocator::memoized_slice`, a naive implementation of
    // `ToOcamlRep` for slices might use `Allocator::memoized`, failing to take
    // the slice length into consideration.
    // Then we'd incorrectly produce the tuple ([1,2,3], [1,2,3]).
    let arena = Arena::new();
    let s1 = &[1usize, 2, 3][..];
    let s2 = &s1[..2];
    let tuple = (s1, s2);
    let ocaml_tuple = arena.add_root(&tuple);

    assert_eq!(
        <(Vec<usize>, Vec<usize>)>::from_ocamlrep(ocaml_tuple),
        Ok((vec![1, 2, 3], vec![1, 2]))
    );
}

#[derive(Debug, PartialEq)]
#[repr(transparent)]
struct U32Pair(u64);

impl U32Pair {
    fn new(fst: u32, snd: u32) -> Self {
        let fst = fst as u64;
        let snd = snd as u64;
        Self(fst << 32 | snd)
    }
    fn fst(&self) -> u32 {
        (self.0 >> 32) as u32
    }
    fn snd(&self) -> u32 {
        self.0 as u32
    }
    fn inner(&self) -> &u64 {
        &self.0
    }
}
impl ToOcamlRep for U32Pair {
    fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> ocamlrep::Value<'a> {
        let mut block = alloc.block_with_size(2);
        alloc.set_field(&mut block, 0, alloc.add_copy(self.fst()));
        alloc.set_field(&mut block, 1, alloc.add_copy(self.snd()));
        block.build()
    }
}
impl FromOcamlRep for U32Pair {
    fn from_ocamlrep(value: ocamlrep::Value<'_>) -> Result<Self, ocamlrep::FromError> {
        let (fst, snd) = <(u32, u32)>::from_ocamlrep(value)?;
        Ok(Self::new(fst, snd))
    }
}

#[test]
fn differently_typed_views_of_same_data() {
    // `Allocator::memoized` is keyed solely off of address and size in bytes.
    // If we have two views of the same bytes, but the views have two different
    // OCaml representations, then `Allocator::add_root` will produce an OCaml
    // value with an unexpected type.
    //
    // Here, `pair_as_int` gets converted first, and memoized. Since its address
    // and size are the same as `pair`, the allocator uses the same memoized
    // OCaml value for both. But the implementations of `ToOcamlRep` and
    // `FromOcamlRep` for U32Pair specify that the OCaml representation is a
    // tuple, not an immediate integer, so we encounter an error when trying to
    // convert the OCaml value to `(u64, U32Pair)`.
    let arena = Arena::new();
    let pair = &U32Pair::new(1, 2);
    let pair_as_int = pair.inner();
    let value = (pair_as_int, pair);

    use ocamlrep::FromError::*;
    assert_eq!(
        <(u64, U32Pair)>::from_ocamlrep(arena.add_root(&value)),
        Err(ErrorInField(1, Box::new(ExpectedBlock(1 << 32 | 2))))
    );

    // Using arena.add instead produces a correct result.
    assert_eq!(
        <(u64, U32Pair)>::from_ocamlrep(arena.add(&value)),
        Ok((1 << 32 | 2, U32Pair::new(1, 2)))
    );
}

#[test]
fn shared_rcs() {
    use std::rc::Rc;

    let arena = Arena::new();
    let inner_tuple = Rc::new((1, 2));
    let outer_tuple = Rc::new((Rc::clone(&inner_tuple), inner_tuple));
    let ocaml_tuple = arena.add_root(&outer_tuple);
    let outer_tuple = ocaml_tuple.as_block().unwrap();

    // The tuple pointer in the first field is physically equal to the tuple
    // pointer in the second field.
    assert_eq!(outer_tuple[0].to_bits(), outer_tuple[1].to_bits());

    let inner_tuple = outer_tuple[0].as_block().unwrap();
    assert_eq!(inner_tuple[0].as_int(), Some(1));
    assert_eq!(inner_tuple[1].as_int(), Some(2));
}

#[test]
fn shared_arcs() {
    use std::sync::Arc;

    let arena = Arena::new();
    let inner_tuple = Arc::new((1, 2));
    let outer_tuple = Arc::new((Arc::clone(&inner_tuple), inner_tuple));
    let ocaml_tuple = arena.add_root(&outer_tuple);
    let outer_tuple = ocaml_tuple.as_block().unwrap();

    // The tuple pointer in the first field is physically equal to the tuple
    // pointer in the second field.
    assert_eq!(outer_tuple[0].to_bits(), outer_tuple[1].to_bits());

    let inner_tuple = outer_tuple[0].as_block().unwrap();
    assert_eq!(inner_tuple[0].as_int(), Some(1));
    assert_eq!(inner_tuple[1].as_int(), Some(2));
}