cpu_endian/
lib.rs

1// Copyright 2020 Shin Yoshida
2//
3// "LGPL-3.0-or-later OR Apache-2.0 OR BSD-2-Clause OR MIT"
4//
5// This is part of cpu-endian
6//
7//  cpu-endian is free software: you can redistribute it and/or modify
8//  it under the terms of the GNU Lesser General Public License as published by
9//  the Free Software Foundation, either version 3 of the License, or
10//  any later version.
11//
12//  cpu-endian is distributed in the hope that it will be useful,
13//  but WITHOUT ANY WARRANTY; without even the implied warranty of
14//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15//  GNU Lesser General Public License for more details.
16//
17//  You should have received a copy of the GNU Lesser General Public License
18//  along with cpu-endian.  If not, see <http://www.gnu.org/licenses/>.
19//
20//
21// Licensed under the Apache License, Version 2.0 (the "License");
22// you may not use this file except in compliance with the License.
23// You may obtain a copy of the License at
24//
25//     http://www.apache.org/licenses/LICENSE-2.0
26//
27// Unless required by applicable law or agreed to in writing, software
28// distributed under the License is distributed on an "AS IS" BASIS,
29// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
30// See the License for the specific language governing permissions and
31// limitations under the License.
32//
33//
34// Redistribution and use in source and binary forms, with or without modification, are permitted
35// provided that the following conditions are met:
36//
37// 1. Redistributions of source code must retain the above copyright notice, this list of
38//    conditions and the following disclaimer.
39// 2. Redistributions in binary form must reproduce the above copyright notice, this
40//    list of conditions and the following disclaimer in the documentation and/or other
41//    materials provided with the distribution.
42//
43// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
44// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
45// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
46// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
47// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
48// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
49// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
50// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
51// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
52// POSSIBILITY OF SUCH DAMAGE.
53//
54//
55// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
56// and associated documentation files (the "Software"), to deal in the Software without
57// restriction, including without limitation the rights to use, copy, modify, merge, publish,
58// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
59// Software is furnished to do so, subject to the following conditions:
60//
61// The above copyright notice and this permission notice (including the next paragraph) shall be
62// included in all copies or substantial portions of the Software.
63//
64// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
65// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
66// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
67// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
68// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
69
70#![deny(missing_docs)]
71
72//! # cpu-endian
73//!
74//! `cpu-endian` is a portable crate to detect CPU byte order.
75//!
76//! It detects how CPU native scalar type is ordered; little-endian or big-endian, or something else (like PDP-endian, mixed-endian, middle-endian, and so on.)
77//!
78//! ## Examples
79//!
80//! ```
81//! use cpu_endian::{Endian, working};
82//!
83//! // Takes first octet of 0x00ff: u16.
84//! let v: u16 = 0x00ff;
85//! let first_octet: u8 = unsafe {
86//!     let ptr = &v as *const u16;
87//!     let ptr = ptr as *const u8;
88//!     *ptr
89//! };
90//!
91//! // If the byte-order is little-endian, the first octet should be 0xff, or if big-endian,
92//! // it should be 0x00.
93//! match working() {
94//!     Endian::Little => assert_eq!(0xff, first_octet),
95//!     Endian::Big => assert_eq!(0x00, first_octet),
96//!     _ => {},
97//! }
98//! ```
99
100use core::cell::Cell;
101
102/// Byte order of scalar types.
103#[derive(Debug, Clone, Copy, PartialEq, Eq)]
104pub enum Endian {
105    /// little-endian
106    Little,
107    /// big-endian
108    Big,
109    /// Neither little-endian nor big-endian. For example, PDP-endian, mixed-endian, middle-endian, and so on.
110    /// (Such endian is very rare today.)
111    Minor,
112}
113
114thread_local!(
115    /// Cache of native_().
116    ///
117    /// Some CPUs change the byte order, however I don't think none of them changes it after process started.
118    static CACHE: Cell<u8> = Cell::new(0)
119);
120
121/// Returns the CPU byte order.
122///
123/// ```
124/// use cpu_endian::*;
125///
126/// // Takes first octet of 0x00ff: u16.
127/// let v: u16 = 0x00ff;
128/// let first_octet: u8 = unsafe {
129///     let ptr = &v as *const u16;
130///     let ptr = ptr as *const u8;
131///     *ptr
132/// };
133///
134/// // If the byte-order is little-endian, the first octet should be 0xff, or if big-endian,
135/// // it should be 0x00.
136/// match working() {
137///     Endian::Little => assert_eq!(0xff, first_octet),
138///     Endian::Big => assert_eq!(0x00, first_octet),
139///     _ => {},
140/// }
141/// ```
142#[inline]
143pub fn working() -> Endian {
144    inner_working()
145}
146
147/// Implementation for `working` .
148///
149/// This function is only for `x86` and `x86_64` architecture, and const function to return
150/// `Endian::Little` . ( `x86` and `x86_64` are typical little-endian CPU.)
151#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
152const fn inner_working() -> Endian {
153    Endian::Little
154}
155
156/// Implementation for `working` .
157///
158/// This function is for CPUs but `x86` nor `x86_64` and calls C function to detect the endian.
159/// (Some CPUs can switch the endian, so it is difficult to hard cord.)
160#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
161#[inline]
162fn inner_working() -> Endian {
163    // No cache is hit.
164    // Because native()_ always returns the same value, Ordering::Relaxed will do.
165    if CACHE.with(|c| c.get()) == 0 {
166        let order = unsafe { native_() };
167        debug_assert_ne!(0, order);
168
169        CACHE.with(|c| c.set(order as u8));
170    }
171
172    match CACHE.with(|c| c.get()) {
173        1 => Endian::Little,
174        2 => Endian::Big,
175        _ => Endian::Minor,
176    }
177}
178
179#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
180use std::os::raw::c_int;
181
182#[link(name = "native_endian_")]
183#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
184extern "C" {
185
186    /// Returns the cpu native endian.
187    ///
188    /// - Little endian: 1
189    /// - Big endian: 2
190    /// - Other: 3
191    fn native_() -> c_int;
192}