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}