#[cfg(any(Py_3_11, not(PyPy)))]
use crate::Py_hash_t;
use crate::{PyObject, Py_UCS1, Py_UCS2, Py_UCS4, Py_ssize_t};
use core::ffi::{c_char, c_int, c_uint, c_void};
use libc::wchar_t;

// skipped PY_UNICODE_TYPE

#[cfg(not(Py_LIMITED_API))]
#[cfg_attr(
    Py_3_13,
    deprecated(note = "Deprecated since Python 3.13. Use `libc::wchar_t` instead.")
)]
pub type Py_UNICODE = wchar_t;

// skipped Py_UNICODE_IS_SURROGATE
// skipped Py_UNICODE_IS_HIGH_SURROGATE
// skipped Py_UNICODE_IS_LOW_SURROGATE
// skipped Py_UNICODE_JOIN_SURROGATES
// skipped Py_UNICODE_HIGH_SURROGATE
// skipped Py_UNICODE_LOW_SURROGATE

// generated by bindgen v0.63.0 (with small adaptations)
#[cfg(not(Py_3_14))]
#[repr(C)]
struct BitfieldUnit<Storage> {
    storage: Storage,
}

#[cfg(not(Py_3_14))]
impl<Storage> BitfieldUnit<Storage> {
    #[inline]
    pub const fn new(storage: Storage) -> Self {
        Self { storage }
    }
}

#[cfg(not(any(GraalPy, Py_3_14)))]
impl<Storage> BitfieldUnit<Storage>
where
    Storage: AsRef<[u8]> + AsMut<[u8]>,
{
    #[inline]
    fn get_bit(&self, index: usize) -> bool {
        debug_assert!(index / 8 < self.storage.as_ref().len());
        let byte_index = index / 8;
        let byte = self.storage.as_ref()[byte_index];
        let bit_index = if cfg!(target_endian = "big") {
            7 - (index % 8)
        } else {
            index % 8
        };
        let mask = 1 << bit_index;
        byte & mask == mask
    }

    #[inline]
    fn set_bit(&mut self, index: usize, val: bool) {
        debug_assert!(index / 8 < self.storage.as_ref().len());
        let byte_index = index / 8;
        let byte = &mut self.storage.as_mut()[byte_index];
        let bit_index = if cfg!(target_endian = "big") {
            7 - (index % 8)
        } else {
            index % 8
        };
        let mask = 1 << bit_index;
        if val {
            *byte |= mask;
        } else {
            *byte &= !mask;
        }
    }

    #[inline]
    fn get(&self, bit_offset: usize, bit_width: u8) -> u64 {
        debug_assert!(bit_width <= 64);
        debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
        debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());
        let mut val = 0;
        for i in 0..(bit_width as usize) {
            if self.get_bit(i + bit_offset) {
                let index = if cfg!(target_endian = "big") {
                    bit_width as usize - 1 - i
                } else {
                    i
                };
                val |= 1 << index;
            }
        }
        val
    }

    #[inline]
    fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) {
        debug_assert!(bit_width <= 64);
        debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
        debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());
        for i in 0..(bit_width as usize) {
            let mask = 1 << i;
            let val_bit_is_set = val & mask == mask;
            let index = if cfg!(target_endian = "big") {
                bit_width as usize - 1 - i
            } else {
                i
            };
            self.set_bit(index + bit_offset, val_bit_is_set);
        }
    }
}

#[cfg(not(any(GraalPy, Py_3_14)))]
const STATE_INTERNED_INDEX: usize = 0;
#[cfg(not(any(GraalPy, Py_3_14)))]
const STATE_INTERNED_WIDTH: u8 = 2;

#[cfg(not(any(GraalPy, Py_3_14)))]
const STATE_KIND_INDEX: usize = STATE_INTERNED_WIDTH as usize;
#[cfg(not(any(GraalPy, Py_3_14)))]
const STATE_KIND_WIDTH: u8 = 3;

#[cfg(not(any(GraalPy, Py_3_14)))]
const STATE_COMPACT_INDEX: usize = (STATE_INTERNED_WIDTH + STATE_KIND_WIDTH) as usize;
#[cfg(not(any(GraalPy, Py_3_14)))]
const STATE_COMPACT_WIDTH: u8 = 1;

#[cfg(not(any(GraalPy, Py_3_14)))]
const STATE_ASCII_INDEX: usize =
    (STATE_INTERNED_WIDTH + STATE_KIND_WIDTH + STATE_COMPACT_WIDTH) as usize;
#[cfg(not(any(GraalPy, Py_3_14)))]
const STATE_ASCII_WIDTH: u8 = 1;

#[cfg(all(not(any(GraalPy, Py_3_14)), Py_3_12))]
const STATE_STATICALLY_ALLOCATED_INDEX: usize =
    (STATE_INTERNED_WIDTH + STATE_KIND_WIDTH + STATE_COMPACT_WIDTH + STATE_ASCII_WIDTH) as usize;
#[cfg(all(not(any(GraalPy, Py_3_14)), Py_3_12))]
const STATE_STATICALLY_ALLOCATED_WIDTH: u8 = 1;

#[cfg(not(any(Py_3_12, GraalPy)))]
const STATE_READY_INDEX: usize =
    (STATE_INTERNED_WIDTH + STATE_KIND_WIDTH + STATE_COMPACT_WIDTH + STATE_ASCII_WIDTH) as usize;
#[cfg(not(any(Py_3_12, GraalPy)))]
const STATE_READY_WIDTH: u8 = 1;

// generated by bindgen v0.63.0 (with small adaptations)
// The same code is generated for Python 3.9, 3.10, and 3.11, but the "ready" field
// has been removed from Python 3.12.

/// Wrapper around the `PyASCIIObject.state` bitfield with getters and setters that work
/// on most little- and big-endian architectures.
///
/// Memory layout of C bitfields is implementation defined, so these functions are still
/// unsafe. Users must verify that they work as expected on the architectures they target.
#[cfg(not(Py_3_14))]
#[repr(C)]
struct PyASCIIObjectState {
    bitfield_align: [u8; 0],
    bitfield: BitfieldUnit<[u8; 4usize]>,
}

// c_uint and u32 are not necessarily the same type on all targets / architectures
#[cfg(not(any(GraalPy, Py_3_14)))]
#[allow(clippy::useless_transmute)]
impl PyASCIIObjectState {
    #[inline]
    unsafe fn interned(&self) -> c_uint {
        core::mem::transmute(
            self.bitfield
                .get(STATE_INTERNED_INDEX, STATE_INTERNED_WIDTH) as u32,
        )
    }

    #[inline]
    unsafe fn set_interned(&mut self, val: c_uint) {
        let val: u32 = core::mem::transmute(val);
        self.bitfield
            .set(STATE_INTERNED_INDEX, STATE_INTERNED_WIDTH, val as u64)
    }

    #[inline]
    unsafe fn kind(&self) -> c_uint {
        core::mem::transmute(self.bitfield.get(STATE_KIND_INDEX, STATE_KIND_WIDTH) as u32)
    }

    #[inline]
    unsafe fn set_kind(&mut self, val: c_uint) {
        let val: u32 = core::mem::transmute(val);
        self.bitfield
            .set(STATE_KIND_INDEX, STATE_KIND_WIDTH, val as u64)
    }

    #[inline]
    unsafe fn compact(&self) -> c_uint {
        core::mem::transmute(self.bitfield.get(STATE_COMPACT_INDEX, STATE_COMPACT_WIDTH) as u32)
    }

    #[inline]
    unsafe fn set_compact(&mut self, val: c_uint) {
        let val: u32 = core::mem::transmute(val);
        self.bitfield
            .set(STATE_COMPACT_INDEX, STATE_COMPACT_WIDTH, val as u64)
    }

    #[inline]
    unsafe fn ascii(&self) -> c_uint {
        core::mem::transmute(self.bitfield.get(STATE_ASCII_INDEX, STATE_ASCII_WIDTH) as u32)
    }

    #[inline]
    unsafe fn set_ascii(&mut self, val: c_uint) {
        let val: u32 = core::mem::transmute(val);
        self.bitfield
            .set(STATE_ASCII_INDEX, STATE_ASCII_WIDTH, val as u64)
    }

    #[cfg(Py_3_12)]
    #[inline]
    unsafe fn statically_allocated(&self) -> c_uint {
        core::mem::transmute(self.bitfield.get(
            STATE_STATICALLY_ALLOCATED_INDEX,
            STATE_STATICALLY_ALLOCATED_WIDTH,
        ) as u32)
    }

    #[cfg(Py_3_12)]
    #[inline]
    unsafe fn set_statically_allocated(&mut self, val: c_uint) {
        let val: u32 = core::mem::transmute(val);
        self.bitfield.set(
            STATE_STATICALLY_ALLOCATED_INDEX,
            STATE_STATICALLY_ALLOCATED_WIDTH,
            val as u64,
        )
    }

    #[cfg(not(Py_3_12))]
    #[inline]
    unsafe fn ready(&self) -> c_uint {
        core::mem::transmute(self.bitfield.get(STATE_READY_INDEX, STATE_READY_WIDTH) as u32)
    }

    #[cfg(not(Py_3_12))]
    #[inline]
    unsafe fn set_ready(&mut self, val: c_uint) {
        let val: u32 = core::mem::transmute(val);
        self.bitfield
            .set(STATE_READY_INDEX, STATE_READY_WIDTH, val as u64)
    }
}

#[cfg(not(Py_3_14))]
impl From<u32> for PyASCIIObjectState {
    #[inline]
    fn from(value: u32) -> Self {
        PyASCIIObjectState {
            bitfield_align: [],
            bitfield: BitfieldUnit::new(value.to_ne_bytes()),
        }
    }
}

#[cfg(not(Py_3_14))]
impl From<PyASCIIObjectState> for u32 {
    #[inline]
    fn from(value: PyASCIIObjectState) -> Self {
        u32::from_ne_bytes(value.bitfield.storage)
    }
}

#[repr(C)]
pub struct PyASCIIObject {
    pub ob_base: PyObject,
    pub length: Py_ssize_t,
    #[cfg(any(Py_3_11, not(PyPy)))]
    pub hash: Py_hash_t,
    /// A bit field with various properties.
    ///
    /// Rust doesn't expose bitfields. So we have accessor functions for
    /// retrieving values.
    ///
    /// Before 3.12:
    /// unsigned int interned:2; // SSTATE_* constants.
    /// unsigned int kind:3;     // PyUnicode_*_KIND constants.
    /// unsigned int compact:1;
    /// unsigned int ascii:1;
    /// unsigned int ready:1;
    /// unsigned int :24;
    ///
    /// 3.12, and 3.13
    /// unsigned int interned:2; // SSTATE_* constants.
    /// unsigned int kind:3;     // PyUnicode_*_KIND constants.
    /// unsigned int compact:1;
    /// unsigned int ascii:1;
    /// unsigned int statically_allocated:1;
    /// unsigned int :24;
    /// on 3.14 and higher PyO3 doesn't access the internal state
    pub state: u32,
    #[cfg(not(Py_3_12))]
    pub wstr: *mut wchar_t,
}

/// Interacting with the bitfield is not actually well-defined, so we mark these APIs unsafe.
#[cfg(not(any(GraalPy, Py_3_14)))]
impl PyASCIIObject {
    #[cfg_attr(not(Py_3_12), allow(rustdoc::broken_intra_doc_links))] // SSTATE_INTERNED_IMMORTAL_STATIC requires 3.12
    /// Get the `interned` field of the [`PyASCIIObject`] state bitfield.
    ///
    /// Returns one of: [`SSTATE_NOT_INTERNED`], [`SSTATE_INTERNED_MORTAL`],
    /// [`SSTATE_INTERNED_IMMORTAL`], or [`SSTATE_INTERNED_IMMORTAL_STATIC`].
    #[inline]
    pub unsafe fn interned(&self) -> c_uint {
        PyASCIIObjectState::from(self.state).interned()
    }

    #[cfg_attr(not(Py_3_12), allow(rustdoc::broken_intra_doc_links))] // SSTATE_INTERNED_IMMORTAL_STATIC requires 3.12
    /// Set the `interned` field of the [`PyASCIIObject`] state bitfield.
    ///
    /// Calling this function with an argument that is not [`SSTATE_NOT_INTERNED`],
    /// [`SSTATE_INTERNED_MORTAL`], [`SSTATE_INTERNED_IMMORTAL`], or
    /// [`SSTATE_INTERNED_IMMORTAL_STATIC`] is invalid.
    #[inline]
    pub unsafe fn set_interned(&mut self, val: c_uint) {
        let mut state = PyASCIIObjectState::from(self.state);
        state.set_interned(val);
        self.state = u32::from(state);
    }

    /// Get the `kind` field of the [`PyASCIIObject`] state bitfield.
    ///
    /// Returns one of:
    #[cfg_attr(not(Py_3_12), doc = "[`PyUnicode_WCHAR_KIND`], ")]
    /// [`PyUnicode_1BYTE_KIND`], [`PyUnicode_2BYTE_KIND`], or [`PyUnicode_4BYTE_KIND`].
    #[inline]
    pub unsafe fn kind(&self) -> c_uint {
        PyASCIIObjectState::from(self.state).kind()
    }

    /// Set the `kind` field of the [`PyASCIIObject`] state bitfield.
    ///
    /// Calling this function with an argument that is not
    #[cfg_attr(not(Py_3_12), doc = "[`PyUnicode_WCHAR_KIND`], ")]
    /// [`PyUnicode_1BYTE_KIND`], [`PyUnicode_2BYTE_KIND`], or [`PyUnicode_4BYTE_KIND`] is invalid.
    #[inline]
    pub unsafe fn set_kind(&mut self, val: c_uint) {
        let mut state = PyASCIIObjectState::from(self.state);
        state.set_kind(val);
        self.state = u32::from(state);
    }

    /// Get the `compact` field of the [`PyASCIIObject`] state bitfield.
    ///
    /// Returns either `0` or `1`.
    #[inline]
    pub unsafe fn compact(&self) -> c_uint {
        PyASCIIObjectState::from(self.state).compact()
    }

    /// Set the `compact` flag of the [`PyASCIIObject`] state bitfield.
    ///
    /// Calling this function with an argument that is neither `0` nor `1` is invalid.
    #[inline]
    pub unsafe fn set_compact(&mut self, val: c_uint) {
        let mut state = PyASCIIObjectState::from(self.state);
        state.set_compact(val);
        self.state = u32::from(state);
    }

    /// Get the `ascii` field of the [`PyASCIIObject`] state bitfield.
    ///
    /// Returns either `0` or `1`.
    #[inline]
    pub unsafe fn ascii(&self) -> c_uint {
        PyASCIIObjectState::from(self.state).ascii()
    }

    /// Set the `ascii` flag of the [`PyASCIIObject`] state bitfield.
    ///
    /// Calling this function with an argument that is neither `0` nor `1` is invalid.
    #[inline]
    #[cfg(not(all(Py_3_14, Py_GIL_DISABLED)))]
    pub unsafe fn set_ascii(&mut self, val: c_uint) {
        let mut state = PyASCIIObjectState::from(self.state);
        state.set_ascii(val);
        self.state = u32::from(state);
    }

    /// Get the `ready` field of the [`PyASCIIObject`] state bitfield.
    ///
    /// Returns either `0` or `1`.
    #[cfg(not(Py_3_12))]
    #[inline]
    pub unsafe fn ready(&self) -> c_uint {
        PyASCIIObjectState::from(self.state).ready()
    }

    /// Set the `ready` flag of the [`PyASCIIObject`] state bitfield.
    ///
    /// Calling this function with an argument that is neither `0` nor `1` is invalid.
    #[cfg(not(Py_3_12))]
    #[inline]
    pub unsafe fn set_ready(&mut self, val: c_uint) {
        let mut state = PyASCIIObjectState::from(self.state);
        state.set_ready(val);
        self.state = u32::from(state);
    }

    /// Get the `statically_allocated` field of the [`PyASCIIObject`] state bitfield.
    ///
    /// Returns either `0` or `1`.
    #[inline]
    #[cfg(Py_3_12)]
    pub unsafe fn statically_allocated(&self) -> c_uint {
        PyASCIIObjectState::from(self.state).statically_allocated()
    }

    /// Set the `statically_allocated` flag of the [`PyASCIIObject`] state bitfield.
    ///
    /// Calling this function with an argument that is neither `0` nor `1` is invalid.
    #[inline]
    #[cfg(Py_3_12)]
    pub unsafe fn set_statically_allocated(&mut self, val: c_uint) {
        let mut state = PyASCIIObjectState::from(self.state);
        state.set_statically_allocated(val);
        self.state = u32::from(state);
    }
}

#[repr(C)]
pub struct PyCompactUnicodeObject {
    pub _base: PyASCIIObject,
    pub utf8_length: Py_ssize_t,
    pub utf8: *mut c_char,
    #[cfg(not(Py_3_12))]
    pub wstr_length: Py_ssize_t,
}

#[repr(C)]
pub union PyUnicodeObjectData {
    pub any: *mut c_void,
    pub latin1: *mut Py_UCS1,
    pub ucs2: *mut Py_UCS2,
    pub ucs4: *mut Py_UCS4,
}

#[repr(C)]
pub struct PyUnicodeObject {
    pub _base: PyCompactUnicodeObject,
    pub data: PyUnicodeObjectData,
}

// skipped private _PyASCIIObject_CAST
// skipped private _PyCompactUnicodeObject_CAST
// skipped private _PyUnicodeObject_CAST

pub const SSTATE_NOT_INTERNED: c_uint = 0;
pub const SSTATE_INTERNED_MORTAL: c_uint = 1;
pub const SSTATE_INTERNED_IMMORTAL: c_uint = 2;
#[cfg(Py_3_12)]
pub const SSTATE_INTERNED_IMMORTAL_STATIC: c_uint = 3;

// skipped PyUnicode_CHECK_INTERNED

#[cfg(any(Py_3_12, GraalPy))]
#[cfg_attr(
    Py_3_14,
    deprecated(note = "Deprecated since Python 3.14. This API does nothing since Python 3.12.")
)]
#[inline]
pub unsafe fn PyUnicode_IS_READY(_op: *mut PyObject) -> c_uint {
    // kept in CPython for backwards compatibility
    1
}

#[cfg(not(any(GraalPy, Py_3_12)))]
#[inline]
pub unsafe fn PyUnicode_IS_READY(op: *mut PyObject) -> c_uint {
    (*(op as *mut PyASCIIObject)).ready()
}

// The following functions depend on the bitfield, which we don't support
// from 3.14 onwards

#[cfg(not(any(GraalPy, Py_3_14)))]
#[inline]
pub unsafe fn PyUnicode_IS_ASCII(op: *mut PyObject) -> c_uint {
    debug_assert!(crate::PyUnicode_Check(op) != 0);
    #[cfg(not(Py_3_12))]
    debug_assert!(PyUnicode_IS_READY(op) != 0);

    (*(op as *mut PyASCIIObject)).ascii()
}

#[cfg(not(any(GraalPy, Py_3_14)))]
#[inline]
pub unsafe fn PyUnicode_IS_COMPACT(op: *mut PyObject) -> c_uint {
    (*(op as *mut PyASCIIObject)).compact()
}

#[cfg(not(any(GraalPy, Py_3_14)))]
#[inline]
pub unsafe fn PyUnicode_IS_COMPACT_ASCII(op: *mut PyObject) -> c_uint {
    ((*(op as *mut PyASCIIObject)).ascii() != 0 && PyUnicode_IS_COMPACT(op) != 0).into()
}

#[cfg(not(Py_3_12))]
#[deprecated(note = "Removed in Python 3.12")]
pub const PyUnicode_WCHAR_KIND: c_uint = 0;

pub const PyUnicode_1BYTE_KIND: c_uint = 1;
pub const PyUnicode_2BYTE_KIND: c_uint = 2;
pub const PyUnicode_4BYTE_KIND: c_uint = 4;

#[cfg(all(not(GraalPy), Py_3_14))]
extern_libpython! {
    #[cfg_attr(PyPy, link_name = "PyPyUnicode_KIND")]
    pub fn PyUnicode_KIND(op: *mut PyObject) -> c_uint;
}

#[cfg(all(not(GraalPy), not(Py_3_14)))]
#[inline]
pub unsafe fn PyUnicode_KIND(op: *mut PyObject) -> c_uint {
    debug_assert!(crate::PyUnicode_Check(op) != 0);
    #[cfg(not(Py_3_12))]
    debug_assert!(PyUnicode_IS_READY(op) != 0);

    (*(op as *mut PyASCIIObject)).kind()
}

#[cfg(not(any(GraalPy, Py_3_14)))]
#[inline]
unsafe fn _PyUnicode_COMPACT_DATA(op: *mut PyObject) -> *mut c_void {
    if PyUnicode_IS_ASCII(op) != 0 {
        (op as *mut PyASCIIObject).offset(1) as *mut c_void
    } else {
        (op as *mut PyCompactUnicodeObject).offset(1) as *mut c_void
    }
}

#[cfg(not(any(GraalPy, PyPy)))]
#[inline]
unsafe fn _PyUnicode_NONCOMPACT_DATA(op: *mut PyObject) -> *mut c_void {
    debug_assert!(!(*(op as *mut PyUnicodeObject)).data.any.is_null());

    (*(op as *mut PyUnicodeObject)).data.any
}

#[cfg(not(any(GraalPy, PyPy, Py_3_14)))]
#[inline]
pub unsafe fn PyUnicode_DATA(op: *mut PyObject) -> *mut c_void {
    debug_assert!(crate::PyUnicode_Check(op) != 0);

    if PyUnicode_IS_COMPACT(op) != 0 {
        _PyUnicode_COMPACT_DATA(op)
    } else {
        _PyUnicode_NONCOMPACT_DATA(op)
    }
}

#[cfg(Py_3_14)]
#[cfg(all(not(GraalPy), Py_3_14))]
extern_libpython! {
    #[cfg_attr(PyPy, link_name = "PyPyUnicode_DATA")]
    pub fn PyUnicode_DATA(op: *mut PyObject) -> *mut c_void;
}

#[cfg(not(any(GraalPy, PyPy)))]
#[inline]
pub unsafe fn PyUnicode_1BYTE_DATA(op: *mut PyObject) -> *mut Py_UCS1 {
    PyUnicode_DATA(op) as *mut Py_UCS1
}

#[cfg(not(any(GraalPy, PyPy)))]
#[inline]
pub unsafe fn PyUnicode_2BYTE_DATA(op: *mut PyObject) -> *mut Py_UCS2 {
    PyUnicode_DATA(op) as *mut Py_UCS2
}

#[cfg(not(any(GraalPy, PyPy)))]
#[inline]
pub unsafe fn PyUnicode_4BYTE_DATA(op: *mut PyObject) -> *mut Py_UCS4 {
    PyUnicode_DATA(op) as *mut Py_UCS4
}

#[cfg(not(GraalPy))]
#[inline]
pub unsafe fn PyUnicode_GET_LENGTH(op: *mut PyObject) -> Py_ssize_t {
    debug_assert!(crate::PyUnicode_Check(op) != 0);
    #[cfg(not(Py_3_12))]
    debug_assert!(PyUnicode_IS_READY(op) != 0);

    (*(op as *mut PyASCIIObject)).length
}

// skipped PyUnstable_Unicode_GET_CACHED_HASH
// skipped PyUnicode_WRITE
// skipped PyUnicode_READ
// skipped PyUnicode_READ_CHAR
// skipped PyUnicode_MAX_CHAR_VALUE

extern_libpython! {
    #[cfg_attr(PyPy, link_name = "PyPyUnicode_New")]
    pub fn PyUnicode_New(size: Py_ssize_t, maxchar: Py_UCS4) -> *mut PyObject;
}

#[cfg(any(Py_3_12, GraalPy))]
#[inline]
pub unsafe fn PyUnicode_READY(_op: *mut PyObject) -> c_int {
    0
}

#[cfg(not(any(Py_3_12, GraalPy)))]
#[inline]
pub unsafe fn PyUnicode_READY(op: *mut PyObject) -> c_int {
    debug_assert!(crate::PyUnicode_Check(op) != 0);

    if PyUnicode_IS_READY(op) != 0 {
        0
    } else {
        _PyUnicode_Ready(op)
    }
}

extern_libpython! {
    #[cfg(not(any(Py_3_12, GraalPy)))]
    #[cfg_attr(PyPy, link_name = "_PyPyUnicode_Ready")]
    fn _PyUnicode_Ready(unicode: *mut PyObject) -> c_int;

    #[cfg(not(PyPy))]
    pub fn PyUnicode_CopyCharacters(
        to: *mut PyObject,
        to_start: Py_ssize_t,
        from: *mut PyObject,
        from_start: Py_ssize_t,
        how_many: Py_ssize_t,
    ) -> Py_ssize_t;

    #[cfg(not(PyPy))]
    pub fn PyUnicode_Fill(
        unicode: *mut PyObject,
        start: Py_ssize_t,
        length: Py_ssize_t,
        fill_char: Py_UCS4,
    ) -> Py_ssize_t;

    #[cfg(not(Py_3_12))]
    #[deprecated]
    #[cfg_attr(PyPy, link_name = "PyPyUnicode_FromUnicode")]
    pub fn PyUnicode_FromUnicode(u: *const wchar_t, size: Py_ssize_t) -> *mut PyObject;

    #[cfg_attr(PyPy, link_name = "PyPyUnicode_FromKindAndData")]
    pub fn PyUnicode_FromKindAndData(
        kind: c_int,
        buffer: *const c_void,
        size: Py_ssize_t,
    ) -> *mut PyObject;

    #[cfg(not(Py_3_12))]
    #[deprecated]
    #[cfg_attr(PyPy, link_name = "PyPyUnicode_AsUnicode")]
    pub fn PyUnicode_AsUnicode(unicode: *mut PyObject) -> *mut wchar_t;

    #[cfg(not(Py_3_12))]
    #[deprecated]
    #[cfg_attr(PyPy, link_name = "PyPyUnicode_AsUnicodeAndSize")]
    pub fn PyUnicode_AsUnicodeAndSize(
        unicode: *mut PyObject,
        size: *mut Py_ssize_t,
    ) -> *mut wchar_t;
}

#[cfg(Py_3_14)]
opaque_struct!(pub PyUnicodeWriter);

extern_libpython! {
    #[cfg(Py_3_14)]
    pub fn PyUnicodeWriter_Create(length: Py_ssize_t) -> *mut PyUnicodeWriter;
    #[cfg(Py_3_14)]
    pub fn PyUnicodeWriter_Discard(writer: *mut PyUnicodeWriter);
    #[cfg(Py_3_14)]
    pub fn PyUnicodeWriter_Finish(writer: *mut PyUnicodeWriter) -> *mut PyObject;

    #[cfg(Py_3_14)]
    pub fn PyUnicodeWriter_WriteChar(writer: *mut PyUnicodeWriter, ch: Py_UCS4) -> c_int;
    #[cfg(Py_3_14)]
    pub fn PyUnicodeWriter_WriteUTF8(
        writer: *mut PyUnicodeWriter,
        str: *const c_char,
        size: Py_ssize_t,
    ) -> c_int;
    // skipped PyUnicodeWriter_WriteASCII
    // skipped PyUnicodeWriter_WriteWideChar
    // skipped PyUnicodeWriter_WriteUCS4

    // skipped PyUnicodeWriter_WriteStr
    // skipped PyUnicodeWriter_WriteRepr
    // skipped PyUnicodeWriter_WriteSubstring
    // skipped PyUnicodeWriter_Format
    // skipped PyUnicodeWriter_DecodeUTF8Stateful
}

// skipped private _PyUnicodeWriter
// skipped private _PyUnicodeWriter_Init
// skipped private _PyUnicodeWriter_Prepare
// skipped private _PyUnicodeWriter_PrepareInternal
// skipped private _PyUnicodeWriter_PrepareKind
// skipped private _PyUnicodeWriter_PrepareKindInternal
// skipped private _PyUnicodeWriter_WriteChar
// skipped private _PyUnicodeWriter_WriteStr
// skipped private _PyUnicodeWriter_WriteSubstring
// skipped private _PyUnicodeWriter_WriteASCIIString
// skipped private _PyUnicodeWriter_WriteLatin1String
// skipped private _PyUnicodeWriter_Finish
// skipped private _PyUnicodeWriter_Dealloc

extern_libpython! {

    #[cfg_attr(PyPy, link_name = "PyPyUnicode_AsUTF8")]
    pub fn PyUnicode_AsUTF8(unicode: *mut PyObject) -> *const c_char;

    // skipped _PyUnicode_AsString

    #[cfg(not(Py_3_11))]
    #[deprecated(note = "use `PyUnicode_AsEncodedString` instead")]
    pub fn PyUnicode_Encode(
        s: *const wchar_t,
        size: Py_ssize_t,
        encoding: *const c_char,
        errors: *const c_char,
    ) -> *mut PyObject;

    #[cfg(not(Py_3_11))]
    #[deprecated(note = "use `PyUnicode_AsEncodedString` instead")]
    pub fn PyUnicode_EncodeUTF7(
        data: *const wchar_t,
        length: Py_ssize_t,
        base64SetO: c_int,
        base64WhiteSpace: c_int,
        errors: *const c_char,
    ) -> *mut PyObject;

    #[cfg(not(Py_3_11))]
    #[deprecated(note = "use `PyUnicode_AsUTF8String` instead")]
    #[cfg_attr(PyPy, link_name = "PyPyUnicode_EncodeUTF8")]
    pub fn PyUnicode_EncodeUTF8(
        data: *const wchar_t,
        length: Py_ssize_t,
        errors: *const c_char,
    ) -> *mut PyObject;

    #[cfg(not(Py_3_11))]
    #[deprecated(note = "use `PyUnicode_AsUTF32String` instead")]
    pub fn PyUnicode_EncodeUTF32(
        data: *const wchar_t,
        length: Py_ssize_t,
        errors: *const c_char,
        byteorder: c_int,
    ) -> *mut PyObject;

    #[cfg(not(Py_3_11))]
    #[deprecated(note = "use `PyUnicode_AsUTF16String` instead")]
    pub fn PyUnicode_EncodeUTF16(
        data: *const wchar_t,
        length: Py_ssize_t,
        errors: *const c_char,
        byteorder: c_int,
    ) -> *mut PyObject;

    #[cfg(not(Py_3_11))]
    #[deprecated(note = "use `PyUnicode_AsUnicodeEscapeString` instead")]
    pub fn PyUnicode_EncodeUnicodeEscape(data: *const wchar_t, length: Py_ssize_t)
        -> *mut PyObject;

    #[cfg(not(Py_3_11))]
    #[deprecated(note = "use `PyUnicode_AsRawUnicodeEscapeString` instead")]
    pub fn PyUnicode_EncodeRawUnicodeEscape(
        data: *const wchar_t,
        length: Py_ssize_t,
    ) -> *mut PyObject;

    #[cfg(not(Py_3_11))]
    #[deprecated(note = "use `PyUnicode_AsLatin1String` instead")]
    #[cfg_attr(PyPy, link_name = "PyPyUnicode_EncodeLatin1")]
    pub fn PyUnicode_EncodeLatin1(
        data: *const wchar_t,
        length: Py_ssize_t,
        errors: *const c_char,
    ) -> *mut PyObject;

    #[cfg(not(Py_3_11))]
    #[deprecated(note = "use `PyUnicode_AsASCIIString` instead")]
    #[cfg_attr(PyPy, link_name = "PyPyUnicode_EncodeASCII")]
    pub fn PyUnicode_EncodeASCII(
        data: *const wchar_t,
        length: Py_ssize_t,
        errors: *const c_char,
    ) -> *mut PyObject;

    #[cfg(not(Py_3_11))]
    #[deprecated(note = "use `PyUnicode_AsCharmapString` instead")]
    pub fn PyUnicode_EncodeCharmap(
        data: *const wchar_t,
        length: Py_ssize_t,
        mapping: *mut PyObject,
        errors: *const c_char,
    ) -> *mut PyObject;

    #[cfg(not(Py_3_11))]
    #[deprecated(note = "use `PyUnicode_Translate` instead")]
    pub fn PyUnicode_TranslateCharmap(
        data: *const wchar_t,
        length: Py_ssize_t,
        table: *mut PyObject,
        errors: *const c_char,
    ) -> *mut PyObject;

    #[cfg(not(Py_3_11))]
    #[deprecated(note = "use `Py_UNICODE_TODECIMAL` instead")]
    #[cfg_attr(PyPy, link_name = "PyPyUnicode_EncodeDecimal")]
    pub fn PyUnicode_EncodeDecimal(
        s: *mut wchar_t,
        length: Py_ssize_t,
        output: *mut c_char,
        errors: *const c_char,
    ) -> c_int;

    #[cfg(not(Py_3_11))]
    #[deprecated(note = "use `Py_UNICODE_TODECIMAL` instead")]
    #[cfg_attr(PyPy, link_name = "PyPyUnicode_TransformDecimalToASCII")]
    pub fn PyUnicode_TransformDecimalToASCII(s: *mut wchar_t, length: Py_ssize_t) -> *mut PyObject;

    // skipped _PyUnicode_IsLowercase
    // skipped _PyUnicode_IsUppercase
    // skipped _PyUnicode_IsTitlecase
    // skipped _PyUnicode_IsWhitespace
    // skipped _PyUnicode_IsLinebreak
    // skipped _PyUnicode_ToLowercase
    // skipped _PyUnicode_ToUppercase
    // skipped _PyUnicode_ToTitlecase

    #[cfg(not(PyPy))]
    fn _PyUnicode_ToDecimalDigit(ch: Py_UCS4) -> c_int;

    // skipped _PyUnicode_ToDigit
    // skipped _PyUnicode_ToNumeric
    // skipped _PyUnicode_IsDecimalDigit
    // skipped _PyUnicode_IsDigit
    // skipped _PyUnicode_IsNumeric
    // skipped _PyUnicode_IsPrintable
    // skipped _PyUnicode_IsAlpha

}

// skipped _Py_ascii_whitespace
// skipped Py_UNICODE_ISSPACE

// skipped Py_UNICODE_ISLOWER
// skipped Py_UNICODE_ISUPPER
// skipped Py_UNICODE_ISTITLE
// skipped Py_UNICODE_ISLINEBREAK

// skipped Py_UNICODE_TOLOWER
// skipped Py_UNICODE_TOUPPER
// skipped Py_UNICODE_TOTITLE

// skipped Py_UNICODE_ISDECIMAL
// skipped Py_UNICODE_ISDIGIT
// skipped Py_UNICODE_ISNUMERIC
// skipped Py_UNICODE_ISPRINTABLE

#[cfg(not(PyPy))]
pub unsafe extern "C" fn Py_UNICODE_TODECIMAL(ch: Py_UCS4) -> c_int {
    _PyUnicode_ToDecimalDigit(ch)
}

#[cfg(PyPy)]
extern_libpython! {
    #[cfg_attr(PyPy, link_name = "PyPy_UNICODE_TODECIMAL")]
    pub fn Py_UNICODE_TODECIMAL(ch: Py_UCS4) -> c_int;
}

// skipped Py_UNICODE_TODIGIT
// skipped Py_UNICODE_TONUMERIC

// skipped Py_UNICODE_ISALPHA

// skipped Py_UNICODE_ISALNUM

// skipped private _PyUnicode_FromId
