Removed openvidu-react-native-androidx
@ -1,499 +0,0 @@
|
||||
/**
|
||||
* Sample React Native App
|
||||
* https://github.com/facebook/react-native
|
||||
*
|
||||
* @format
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import {
|
||||
Platform,
|
||||
TextInput,
|
||||
ScrollView,
|
||||
Button,
|
||||
Alert,
|
||||
Linking,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
Image,
|
||||
PermissionsAndroid,
|
||||
} from 'react-native';
|
||||
|
||||
import { OpenVidu } from 'openvidu-browser';
|
||||
import { RTCView } from './node_modules/openvidu-browser/node_modules/react-native-webrtc';
|
||||
import axios from 'axios';
|
||||
|
||||
const OPENVIDU_SERVER_URL = 'https://demos.openvidu.io:4443';
|
||||
const OPENVIDU_SERVER_SECRET = 'MY_SECRET';
|
||||
|
||||
|
||||
type Props = {};
|
||||
export default class App extends Component<Props> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
mySessionId: 'SessionA',
|
||||
myUserName: 'Participant' + Math.floor(Math.random() * 100),
|
||||
session: undefined,
|
||||
mainStreamManager: undefined,
|
||||
subscribers: [],
|
||||
role: 'PUBLISHER',
|
||||
mirror: true,
|
||||
videoSource: undefined,
|
||||
camera: true,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {}
|
||||
|
||||
componentWillUnmount() {
|
||||
//this.leaveSession();
|
||||
}
|
||||
|
||||
async checkAndroidPermissions() {
|
||||
try {
|
||||
const camera = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.CAMERA, {
|
||||
title: 'Camera Permission',
|
||||
message: 'OpenVidu needs access to your camera',
|
||||
buttonNeutral: 'Ask Me Later',
|
||||
buttonNegative: 'Cancel',
|
||||
buttonPositive: 'OK',
|
||||
});
|
||||
const audio = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.RECORD_AUDIO, {
|
||||
title: 'Aduio Permission',
|
||||
message: 'OpenVidu needs access to your microphone',
|
||||
buttonNeutral: 'Ask Me Later',
|
||||
buttonNegative: 'Cancel',
|
||||
buttonPositive: 'OK',
|
||||
});
|
||||
const storage = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE, {
|
||||
title: 'STORAGE',
|
||||
message: 'OpenVidu needs access to your storage ',
|
||||
buttonNeutral: 'Ask Me Later',
|
||||
buttonNegative: 'Cancel',
|
||||
buttonPositive: 'OK',
|
||||
});
|
||||
if (camera === PermissionsAndroid.RESULTS.GRANTED) {
|
||||
console.log('You can use the camera');
|
||||
} else {
|
||||
console.log('Camera permission denied');
|
||||
}
|
||||
if (audio === PermissionsAndroid.RESULTS.GRANTED) {
|
||||
console.log('You can use the audio');
|
||||
} else {
|
||||
console.log('audio permission denied');
|
||||
}
|
||||
if (storage === PermissionsAndroid.RESULTS.GRANTED) {
|
||||
console.log('You can use the storage');
|
||||
} else {
|
||||
console.log('storage permission denied');
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(err);
|
||||
}
|
||||
}
|
||||
|
||||
joinSession() {
|
||||
// --- 1) Get an OpenVidu object ---
|
||||
|
||||
this.OV = new OpenVidu();
|
||||
|
||||
// --- 2) Init a session ---
|
||||
|
||||
this.setState(
|
||||
{
|
||||
session: this.OV.initSession(),
|
||||
},
|
||||
() => {
|
||||
var mySession = this.state.session;
|
||||
// --- 3) Specify the actions when events take place in the session ---
|
||||
|
||||
// On every new Stream received...
|
||||
mySession.on('streamCreated', (event) => {
|
||||
// Subscribe to the Stream to receive it. Second parameter is undefined
|
||||
// so OpenVidu doesn't create an HTML video by its own
|
||||
const subscriber = mySession.subscribe(event.stream, undefined);
|
||||
|
||||
var subscribers = this.state.subscribers;
|
||||
|
||||
subscribers.push(subscriber);
|
||||
// Update the state with the new subscribers
|
||||
this.setState({
|
||||
subscribers: subscribers,
|
||||
});
|
||||
});
|
||||
|
||||
// On every Stream destroyed...
|
||||
mySession.on('streamDestroyed', (event) => {
|
||||
event.preventDefault();
|
||||
// Remove the stream from 'subscribers' array
|
||||
this.deleteSubscriber(event.stream.streamManager);
|
||||
});
|
||||
|
||||
// --- 4) Connect to the session with a valid user token ---
|
||||
// 'getToken' method is simulating what your server-side should do.
|
||||
// 'token' parameter should be retrieved and returned by your own backend
|
||||
this.getToken()
|
||||
.then((token) => {
|
||||
// First param is the token got from OpenVidu Server. Second param can be retrieved by every user on event
|
||||
// 'streamCreated' (property Stream.connection.data), and will be appended to DOM as the user's nickname
|
||||
mySession
|
||||
.connect(token, { clientData: this.state.myUserName })
|
||||
.then(() => {
|
||||
if (Platform.OS == 'android') {
|
||||
this.checkAndroidPermissions();
|
||||
}
|
||||
|
||||
// --- 5) Get your own camera stream ---
|
||||
if (this.state.role !== 'SUBSCRIBER') {
|
||||
const properties = {
|
||||
audioSource: undefined, // The source of audio. If undefined default microphone
|
||||
videoSource: undefined, // The source of video. If undefined default webcam
|
||||
publishAudio: true, // Whether you want to start publishing with your audio unmuted or not
|
||||
publishVideo: true, // Whether you want to start publishing with your video enabled or not
|
||||
resolution: '640x480', // The resolution of your video
|
||||
frameRate: 30, // The frame rate of your video
|
||||
insertMode: 'APPEND', // How the video is inserted in the target element 'video-container'
|
||||
};
|
||||
// Init a publisher passing undefined as targetElement (we don't want OpenVidu to insert a video
|
||||
// element: we will manage it on our own) and with the desired properties
|
||||
let publisher = this.OV.initPublisher(undefined, properties);
|
||||
|
||||
// --- 6) Publish your stream ---
|
||||
|
||||
// Set the main video in the page to display our webcam and store our Publisher
|
||||
this.setState({
|
||||
mainStreamManager: publisher,
|
||||
videoSource: !properties.videoSource ? '1' : properties.videoSource, // 0: back camera | 1: user camera |
|
||||
});
|
||||
mySession.publish(publisher);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log('There was an error connecting to the session:', error.code, error.message);
|
||||
});
|
||||
})
|
||||
.catch((error) => console.log('Error', error));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
getNicknameTag(stream) {
|
||||
// Gets the nickName of the user
|
||||
if (stream.connection && JSON.parse(stream.connection.data) && JSON.parse(stream.connection.data).clientData) {
|
||||
return JSON.parse(stream.connection.data).clientData;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
deleteSubscriber(streamManager) {
|
||||
setTimeout(() => {
|
||||
let subscribers = this.state.subscribers;
|
||||
const index = subscribers.indexOf(streamManager, 0);
|
||||
if (index > -1) {
|
||||
subscribers.splice(index, 1);
|
||||
this.setState({
|
||||
subscribers: subscribers,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
leaveSession() {
|
||||
// --- 7) Leave the session by calling 'disconnect' method over the Session object ---
|
||||
|
||||
const mySession = this.state.session;
|
||||
|
||||
if (mySession) {
|
||||
mySession.disconnect();
|
||||
}
|
||||
|
||||
// Empty all properties...
|
||||
setTimeout(() => {
|
||||
this.OV = null;
|
||||
this.setState({
|
||||
session: undefined,
|
||||
subscribers: [],
|
||||
mySessionId: 'SessionA',
|
||||
myUserName: 'Participant' + Math.floor(Math.random() * 100),
|
||||
mainStreamManager: undefined,
|
||||
publisher: undefined,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
toggleCamera() {
|
||||
/**
|
||||
* _switchCamera() Method provided by react-native-webrtc:
|
||||
* This function allows to switch the front / back cameras in a video track on the fly, without the need for adding / removing tracks or renegotiating
|
||||
*/
|
||||
|
||||
this.state.mainStreamManager.stream
|
||||
.getMediaStream()
|
||||
.getVideoTracks()[0]
|
||||
._switchCamera();
|
||||
this.setState({ mirror: !this.state.mirror });
|
||||
|
||||
/**
|
||||
* Traditional way:
|
||||
* Renegotiating stream and init new publisher to change the camera
|
||||
*/
|
||||
/*
|
||||
this.OV.getDevices().then(devices => {
|
||||
console.log("DEVICES => ", devices);
|
||||
let device = devices.filter(device => device.kind === 'videoinput' && device.deviceId !== this.state.videoSource)[0]
|
||||
const properties = {
|
||||
audioSource: undefined,
|
||||
videoSource: device.deviceId,
|
||||
publishAudio: true,
|
||||
publishVideo: true,
|
||||
resolution: '640x480',
|
||||
frameRate: 30,
|
||||
insertMode: 'APPEND',
|
||||
}
|
||||
|
||||
let publisher = this.OV.initPublisher(undefined, properties);
|
||||
|
||||
this.state.session.unpublish(this.state.mainStreamManager);
|
||||
|
||||
this.setState({
|
||||
videoSource : device.deviceId,
|
||||
mainStreamManager: publisher,
|
||||
mirror: !this.state.mirror
|
||||
});
|
||||
this.state.session.publish(publisher);
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
muteUnmuteCamera() {
|
||||
this.state.mainStreamManager.publishVideo(!this.state.camera);
|
||||
this.setState({ camera: !this.state.camera });
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ScrollView>
|
||||
{this.state.mainStreamManager ? (
|
||||
<View>
|
||||
<View style={styles.container}>
|
||||
<Text>Session: {this.state.mySessionId}</Text>
|
||||
<Text>{this.getNicknameTag(this.state.mainStreamManager.stream)}</Text>
|
||||
<RTCView
|
||||
zOrder={0}
|
||||
objectFit="cover"
|
||||
mirror={this.state.mirror}
|
||||
ref={(rtcVideo) => {
|
||||
if (!!rtcVideo) {
|
||||
this.state.mainStreamManager.addVideoElement(rtcVideo);
|
||||
}
|
||||
}}
|
||||
style={styles.selfView}
|
||||
/>
|
||||
</View>
|
||||
<View>
|
||||
<View style={styles.button}>
|
||||
<Button
|
||||
onLongPress={() => this.toggleCamera()}
|
||||
onPress={() => this.toggleCamera()}
|
||||
title="Toggle Camera"
|
||||
color="#841584"
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.button}>
|
||||
<Button
|
||||
onLongPress={() => this.muteUnmuteCamera()}
|
||||
onPress={() => this.muteUnmuteCamera()}
|
||||
title={this.state.camera ? 'Mute Camera' : 'Unmute Camera'}
|
||||
color="#00cbff"
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View style={styles.button}>
|
||||
<Button
|
||||
onLongPress={() => this.leaveSession()}
|
||||
onPress={() => this.leaveSession()}
|
||||
title="Leave Session"
|
||||
color="#ff0000"
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
) : (
|
||||
<View>
|
||||
<View style={{
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
padding: 20}}>
|
||||
|
||||
<Image style={styles.img} source={require('./resources/images/openvidu_grey_bg_transp_cropped.png')} />
|
||||
</View>
|
||||
<View style={{ justifyContent: 'center', alignItems: 'center'}}>
|
||||
<TextInput
|
||||
style={{ width: '90%', height: 40, borderColor: 'gray', borderWidth: 1 }}
|
||||
onChangeText={(mySessionId) => this.setState({ mySessionId })}
|
||||
value={this.state.mySessionId}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View style={styles.button}>
|
||||
<Button
|
||||
onLongPress={() => this.joinSession()}
|
||||
onPress={() => this.joinSession()}
|
||||
title="Join"
|
||||
color="#841584"
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
|
||||
<View style={[styles.container, { flexDirection: 'row', flexWrap: 'wrap' }]}>
|
||||
{this.state.subscribers.map((item, index) => {
|
||||
if (!!item) {
|
||||
return (
|
||||
<View key={index}>
|
||||
<Text>{this.getNicknameTag(item.stream)}</Text>
|
||||
<RTCView
|
||||
zOrder={0}
|
||||
objectFit="cover"
|
||||
style={styles.remoteView}
|
||||
ref={(rtcVideo) => {
|
||||
if (!!rtcVideo) {
|
||||
item.addVideoElement(rtcVideo);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* --------------------------
|
||||
* SERVER-SIDE RESPONSIBILITY
|
||||
* --------------------------
|
||||
* These methods retrieve the mandatory user token from OpenVidu Server.
|
||||
* This behavior MUST BE IN YOUR SERVER-SIDE IN PRODUCTION (by using
|
||||
* the API REST, openvidu-java-client or openvidu-node-client):
|
||||
* 1) Initialize a session in OpenVidu Server (POST /api/sessions)
|
||||
* 2) Generate a token in OpenVidu Server (POST /api/tokens)
|
||||
* 3) The token must be consumed in Session.connect() method
|
||||
*/
|
||||
|
||||
getToken() {
|
||||
return this.createSession(this.state.mySessionId)
|
||||
.then((sessionId) => this.createToken(sessionId))
|
||||
.catch((error) => console.log(error));
|
||||
}
|
||||
|
||||
createSession(sessionId) {
|
||||
return new Promise((resolve) => {
|
||||
var data = JSON.stringify({ customSessionId: sessionId });
|
||||
axios
|
||||
.post(OPENVIDU_SERVER_URL + '/api/sessions', data, {
|
||||
headers: {
|
||||
Authorization: 'Basic ' + btoa('OPENVIDUAPP:' + OPENVIDU_SERVER_SECRET),
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
},
|
||||
})
|
||||
.then((response) => {
|
||||
console.log('CREATE SESION', response);
|
||||
resolve(response.data.id);
|
||||
})
|
||||
.catch((response) => {
|
||||
console.log(response);
|
||||
var error = Object.assign({}, response);
|
||||
if (!error.response) {
|
||||
console.error("Network error: ", error);
|
||||
if( error.request && error.request._response){
|
||||
console.error("Response of the request: ", error.request._response);
|
||||
}
|
||||
}
|
||||
else if (error.response && error.response.status && error.response.status === 409) {
|
||||
console.log('RESOLVING WITH SESSIONID, 409');
|
||||
resolve(sessionId);
|
||||
} else {
|
||||
console.warn(
|
||||
'No connection to OpenVidu Server. This may be a certificate error at ' + OPENVIDU_SERVER_URL,
|
||||
);
|
||||
|
||||
Alert.alert(
|
||||
'No connection to OpenVidu Server.',
|
||||
'This may be a certificate error at "' +
|
||||
OPENVIDU_SERVER_URL +
|
||||
'"\n\nClick OK to navigate and accept it. ' +
|
||||
'If no certificate warning is shown, then check that your OpenVidu Server is up and running at "' +
|
||||
OPENVIDU_SERVER_URL +
|
||||
'"',
|
||||
[
|
||||
{
|
||||
text: 'Cancel',
|
||||
onPress: () => console.log('Cancel Pressed'),
|
||||
style: 'cancel',
|
||||
},
|
||||
{
|
||||
text: 'OK',
|
||||
onPress: () =>
|
||||
Linking.openURL(OPENVIDU_SERVER_URL + '/accept-certificate').catch((err) =>
|
||||
console.error('An error occurred', err),
|
||||
),
|
||||
},
|
||||
],
|
||||
{ cancelable: false },
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
createToken(sessionId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
var data = JSON.stringify({ session: sessionId });
|
||||
axios
|
||||
.post(OPENVIDU_SERVER_URL + '/api/tokens', data, {
|
||||
headers: {
|
||||
Authorization: 'Basic ' + btoa('OPENVIDUAPP:' + OPENVIDU_SERVER_SECRET),
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
.then((response) => {
|
||||
console.log('TOKEN', response);
|
||||
resolve(response.data.token);
|
||||
})
|
||||
.catch((error) => reject(error));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
paddingTop: Platform.OS == 'ios' ? 20 : 0,
|
||||
},
|
||||
selfView: {
|
||||
width: '100%',
|
||||
height: 300,
|
||||
},
|
||||
remoteView: {
|
||||
width: 150,
|
||||
height: 150,
|
||||
},
|
||||
button: {
|
||||
padding: 10,
|
||||
},
|
||||
img: {
|
||||
flex: 1,
|
||||
width: 400,
|
||||
height: 200,
|
||||
}
|
||||
});
|
||||
@ -1,14 +0,0 @@
|
||||
/**
|
||||
* @format
|
||||
*/
|
||||
|
||||
import 'react-native';
|
||||
import React from 'react';
|
||||
import App from '../App';
|
||||
|
||||
// Note: test renderer must be required after react-native.
|
||||
import renderer from 'react-test-renderer';
|
||||
|
||||
it('renders correctly', () => {
|
||||
renderer.create(<App />);
|
||||
});
|
||||
@ -1,55 +0,0 @@
|
||||
# To learn about Buck see [Docs](https://buckbuild.com/).
|
||||
# To run your application with Buck:
|
||||
# - install Buck
|
||||
# - `npm start` - to start the packager
|
||||
# - `cd android`
|
||||
# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
|
||||
# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
|
||||
# - `buck install -r android/app` - compile, install and run application
|
||||
#
|
||||
|
||||
load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
|
||||
|
||||
lib_deps = []
|
||||
|
||||
create_aar_targets(glob(["libs/*.aar"]))
|
||||
|
||||
create_jar_targets(glob(["libs/*.jar"]))
|
||||
|
||||
android_library(
|
||||
name = "all-libs",
|
||||
exported_deps = lib_deps,
|
||||
)
|
||||
|
||||
android_library(
|
||||
name = "app-code",
|
||||
srcs = glob([
|
||||
"src/main/java/**/*.java",
|
||||
]),
|
||||
deps = [
|
||||
":all-libs",
|
||||
":build_config",
|
||||
":res",
|
||||
],
|
||||
)
|
||||
|
||||
android_build_config(
|
||||
name = "build_config",
|
||||
package = "com.openvidu_react_native_androidx",
|
||||
)
|
||||
|
||||
android_resource(
|
||||
name = "res",
|
||||
package = "com.openvidu_react_native_androidx",
|
||||
res = "src/main/res",
|
||||
)
|
||||
|
||||
android_binary(
|
||||
name = "app",
|
||||
keystore = "//android/keystores:debug",
|
||||
manifest = "src/main/AndroidManifest.xml",
|
||||
package_type = "debug",
|
||||
deps = [
|
||||
":app-code",
|
||||
],
|
||||
)
|
||||
@ -1,210 +0,0 @@
|
||||
apply plugin: "com.android.application"
|
||||
|
||||
import com.android.build.OutputFile
|
||||
|
||||
/**
|
||||
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
|
||||
* and bundleReleaseJsAndAssets).
|
||||
* These basically call `react-native bundle` with the correct arguments during the Android build
|
||||
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
|
||||
* bundle directly from the development server. Below you can see all the possible configurations
|
||||
* and their defaults. If you decide to add a configuration block, make sure to add it before the
|
||||
* `apply from: "../../node_modules/react-native/react.gradle"` line.
|
||||
*
|
||||
* project.ext.react = [
|
||||
* // the name of the generated asset file containing your JS bundle
|
||||
* bundleAssetName: "index.android.bundle",
|
||||
*
|
||||
* // the entry file for bundle generation
|
||||
* entryFile: "index.android.js",
|
||||
*
|
||||
* // https://facebook.github.io/react-native/docs/performance#enable-the-ram-format
|
||||
* bundleCommand: "ram-bundle",
|
||||
*
|
||||
* // whether to bundle JS and assets in debug mode
|
||||
* bundleInDebug: false,
|
||||
*
|
||||
* // whether to bundle JS and assets in release mode
|
||||
* bundleInRelease: true,
|
||||
*
|
||||
* // whether to bundle JS and assets in another build variant (if configured).
|
||||
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
|
||||
* // The configuration property can be in the following formats
|
||||
* // 'bundleIn${productFlavor}${buildType}'
|
||||
* // 'bundleIn${buildType}'
|
||||
* // bundleInFreeDebug: true,
|
||||
* // bundleInPaidRelease: true,
|
||||
* // bundleInBeta: true,
|
||||
*
|
||||
* // whether to disable dev mode in custom build variants (by default only disabled in release)
|
||||
* // for example: to disable dev mode in the staging build type (if configured)
|
||||
* devDisabledInStaging: true,
|
||||
* // The configuration property can be in the following formats
|
||||
* // 'devDisabledIn${productFlavor}${buildType}'
|
||||
* // 'devDisabledIn${buildType}'
|
||||
*
|
||||
* // the root of your project, i.e. where "package.json" lives
|
||||
* root: "../../",
|
||||
*
|
||||
* // where to put the JS bundle asset in debug mode
|
||||
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
|
||||
*
|
||||
* // where to put the JS bundle asset in release mode
|
||||
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
|
||||
*
|
||||
* // where to put drawable resources / React Native assets, e.g. the ones you use via
|
||||
* // require('./image.png')), in debug mode
|
||||
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
|
||||
*
|
||||
* // where to put drawable resources / React Native assets, e.g. the ones you use via
|
||||
* // require('./image.png')), in release mode
|
||||
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
|
||||
*
|
||||
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
|
||||
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
|
||||
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
|
||||
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
|
||||
* // for example, you might want to remove it from here.
|
||||
* inputExcludes: ["android/**", "ios/**"],
|
||||
*
|
||||
* // override which node gets called and with what additional arguments
|
||||
* nodeExecutableAndArgs: ["node"],
|
||||
*
|
||||
* // supply additional arguments to the packager
|
||||
* extraPackagerArgs: []
|
||||
* ]
|
||||
*/
|
||||
|
||||
project.ext.react = [
|
||||
entryFile: "index.js",
|
||||
enableHermes: false, // clean and rebuild if changing
|
||||
]
|
||||
|
||||
apply from: "../../node_modules/react-native/react.gradle"
|
||||
|
||||
/**
|
||||
* Set this to true to create two separate APKs instead of one:
|
||||
* - An APK that only works on ARM devices
|
||||
* - An APK that only works on x86 devices
|
||||
* The advantage is the size of the APK is reduced by about 4MB.
|
||||
* Upload all the APKs to the Play Store and people will download
|
||||
* the correct one based on the CPU architecture of their device.
|
||||
*/
|
||||
def enableSeparateBuildPerCPUArchitecture = false
|
||||
|
||||
/**
|
||||
* Run Proguard to shrink the Java bytecode in release builds.
|
||||
*/
|
||||
def enableProguardInReleaseBuilds = false
|
||||
|
||||
/**
|
||||
* The preferred build flavor of JavaScriptCore.
|
||||
*
|
||||
* For example, to use the international variant, you can use:
|
||||
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
|
||||
*
|
||||
* The international variant includes ICU i18n library and necessary data
|
||||
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
|
||||
* give correct results when using with locales other than en-US. Note that
|
||||
* this variant is about 6MiB larger per architecture than default.
|
||||
*/
|
||||
def jscFlavor = 'org.webkit:android-jsc:+'
|
||||
|
||||
/**
|
||||
* Whether to enable the Hermes VM.
|
||||
*
|
||||
* This should be set on project.ext.react and mirrored here. If it is not set
|
||||
* on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
|
||||
* and the benefits of using Hermes will therefore be sharply reduced.
|
||||
*/
|
||||
def enableHermes = project.ext.react.get("enableHermes", false);
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.openvidu_react_native_androidx"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
splits {
|
||||
abi {
|
||||
reset()
|
||||
enable enableSeparateBuildPerCPUArchitecture
|
||||
universalApk false // If true, also generate a universal APK
|
||||
include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
||||
}
|
||||
}
|
||||
signingConfigs {
|
||||
debug {
|
||||
storeFile file('debug.keystore')
|
||||
storePassword 'android'
|
||||
keyAlias 'androiddebugkey'
|
||||
keyPassword 'android'
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
debug {
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
release {
|
||||
// Caution! In production, you need to generate your own keystore file.
|
||||
// see https://facebook.github.io/react-native/docs/signed-apk-android.
|
||||
signingConfig signingConfigs.debug
|
||||
minifyEnabled enableProguardInReleaseBuilds
|
||||
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
||||
}
|
||||
}
|
||||
// applicationVariants are e.g. debug, release
|
||||
applicationVariants.all { variant ->
|
||||
variant.outputs.each { output ->
|
||||
// For each separate APK per architecture, set a unique version code as described here:
|
||||
// https://developer.android.com/studio/build/configure-apk-splits.html
|
||||
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
|
||||
def abi = output.getFilter(OutputFile.ABI)
|
||||
if (abi != null) { // null for the universal-debug, universal-release variants
|
||||
output.versionCodeOverride =
|
||||
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
pickFirst '**/armeabi-v7a/libc++_shared.so'
|
||||
pickFirst '**/x86/libc++_shared.so'
|
||||
pickFirst '**/arm64-v8a/libc++_shared.so'
|
||||
pickFirst '**/x86_64/libc++_shared.so'
|
||||
pickFirst '**/x86/libjsc.so'
|
||||
pickFirst '**/armeabi-v7a/libjsc.so'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
implementation "com.facebook.react:react-native:+" // From node_modules
|
||||
|
||||
if (enableHermes) {
|
||||
def hermesPath = "../../node_modules/hermesvm/android/";
|
||||
debugImplementation files(hermesPath + "hermes-debug.aar")
|
||||
releaseImplementation files(hermesPath + "hermes-release.aar")
|
||||
} else {
|
||||
implementation jscFlavor
|
||||
}
|
||||
}
|
||||
|
||||
// Run this once to be able to run the application with BUCK
|
||||
// puts all compile dependencies into folder libs for BUCK to use
|
||||
task copyDownloadableDepsToLibs(type: Copy) {
|
||||
from configurations.compile
|
||||
into 'libs'
|
||||
}
|
||||
|
||||
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
|
||||
@ -1,19 +0,0 @@
|
||||
"""Helper definitions to glob .aar and .jar targets"""
|
||||
|
||||
def create_aar_targets(aarfiles):
|
||||
for aarfile in aarfiles:
|
||||
name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
|
||||
lib_deps.append(":" + name)
|
||||
android_prebuilt_aar(
|
||||
name = name,
|
||||
aar = aarfile,
|
||||
)
|
||||
|
||||
def create_jar_targets(jarfiles):
|
||||
for jarfile in jarfiles:
|
||||
name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
|
||||
lib_deps.append(":" + name)
|
||||
prebuilt_jar(
|
||||
name = name,
|
||||
binary_jar = jarfile,
|
||||
)
|
||||
@ -1,10 +0,0 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
|
||||
<application android:usesCleartextTraffic="true" tools:targetApi="28" tools:ignore="GoogleAppIndexingWarning" />
|
||||
</manifest>
|
||||
@ -1,26 +0,0 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.openvidu_react_native_androidx">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:name=".MainApplication"
|
||||
android:label="@string/app_name"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:allowBackup="false"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@ -1,15 +0,0 @@
|
||||
package com.openvidu_react_native_androidx;
|
||||
|
||||
import com.facebook.react.ReactActivity;
|
||||
|
||||
public class MainActivity extends ReactActivity {
|
||||
|
||||
/**
|
||||
* Returns the name of the main component registered from JavaScript.
|
||||
* This is used to schedule rendering of the component.
|
||||
*/
|
||||
@Override
|
||||
protected String getMainComponentName() {
|
||||
return "openvidu_react_native_androidx";
|
||||
}
|
||||
}
|
||||
@ -1,49 +0,0 @@
|
||||
package com.openvidu_react_native_androidx;
|
||||
|
||||
import android.app.Application;
|
||||
import android.util.Log;
|
||||
|
||||
import com.facebook.react.PackageList;
|
||||
import com.facebook.hermes.reactexecutor.HermesExecutorFactory;
|
||||
import com.facebook.react.bridge.JavaScriptExecutorFactory;
|
||||
import com.facebook.react.ReactApplication;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class MainApplication extends Application implements ReactApplication {
|
||||
|
||||
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
|
||||
@Override
|
||||
public boolean getUseDeveloperSupport() {
|
||||
return BuildConfig.DEBUG;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ReactPackage> getPackages() {
|
||||
@SuppressWarnings("UnnecessaryLocalVariable")
|
||||
List<ReactPackage> packages = new PackageList(this).getPackages();
|
||||
// Packages that cannot be autolinked yet can be added manually here, for example:
|
||||
// packages.add(new MyReactNativePackage());
|
||||
return packages;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getJSMainModuleName() {
|
||||
return "index";
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public ReactNativeHost getReactNativeHost() {
|
||||
return mReactNativeHost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
SoLoader.init(this, /* native exopackage */ false);
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 15 KiB |
@ -1,3 +0,0 @@
|
||||
<resources>
|
||||
<string name="app_name">openvidu_react_native_androidx</string>
|
||||
</resources>
|
||||
@ -1,9 +0,0 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="android:textColor">#000000</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
@ -1,38 +0,0 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext {
|
||||
buildToolsVersion = "28.0.3"
|
||||
minSdkVersion = 16
|
||||
compileSdkVersion = 28
|
||||
targetSdkVersion = 28
|
||||
supportLibVersion = "28.0.0"
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath("com.android.tools.build:gradle:3.4.1")
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven {
|
||||
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
||||
url("$rootDir/../node_modules/react-native/android")
|
||||
}
|
||||
maven {
|
||||
// Android JSC is installed from npm
|
||||
url("$rootDir/../node_modules/jsc-android/dist")
|
||||
}
|
||||
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
# Project-wide Gradle settings.
|
||||
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
# Default value: -Xmx10248m -XX:MaxPermSize=256m
|
||||
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
@ -1,5 +0,0 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
188
openvidu-react-native-androidx/android/gradlew
vendored
@ -1,188 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
100
openvidu-react-native-androidx/android/gradlew.bat
vendored
@ -1,100 +0,0 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem http://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@ -1,3 +0,0 @@
|
||||
rootProject.name = 'openvidu_react_native_androidx'
|
||||
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
|
||||
include ':app'
|
||||
@ -1,4 +0,0 @@
|
||||
{
|
||||
"name": "openvidu_react_native_androidx",
|
||||
"displayName": "openvidu_react_native_androidx"
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
presets: ['module:metro-react-native-babel-preset'],
|
||||
};
|
||||
@ -1,9 +0,0 @@
|
||||
/**
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {AppRegistry} from 'react-native';
|
||||
import App from './App';
|
||||
import {name as appName} from './app.json';
|
||||
|
||||
AppRegistry.registerComponent(appName, () => App);
|
||||
@ -1,187 +0,0 @@
|
||||
PODS:
|
||||
- boost-for-react-native (1.63.0)
|
||||
- DoubleConversion (1.1.6)
|
||||
- Folly (2018.10.22.00):
|
||||
- boost-for-react-native
|
||||
- DoubleConversion
|
||||
- Folly/Default (= 2018.10.22.00)
|
||||
- glog
|
||||
- Folly/Default (2018.10.22.00):
|
||||
- boost-for-react-native
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- glog (0.3.5)
|
||||
- React (0.60.5):
|
||||
- React-Core (= 0.60.5)
|
||||
- React-DevSupport (= 0.60.5)
|
||||
- React-RCTActionSheet (= 0.60.5)
|
||||
- React-RCTAnimation (= 0.60.5)
|
||||
- React-RCTBlob (= 0.60.5)
|
||||
- React-RCTImage (= 0.60.5)
|
||||
- React-RCTLinking (= 0.60.5)
|
||||
- React-RCTNetwork (= 0.60.5)
|
||||
- React-RCTSettings (= 0.60.5)
|
||||
- React-RCTText (= 0.60.5)
|
||||
- React-RCTVibration (= 0.60.5)
|
||||
- React-RCTWebSocket (= 0.60.5)
|
||||
- React-Core (0.60.5):
|
||||
- Folly (= 2018.10.22.00)
|
||||
- React-cxxreact (= 0.60.5)
|
||||
- React-jsiexecutor (= 0.60.5)
|
||||
- yoga (= 0.60.5.React)
|
||||
- React-cxxreact (0.60.5):
|
||||
- boost-for-react-native (= 1.63.0)
|
||||
- DoubleConversion
|
||||
- Folly (= 2018.10.22.00)
|
||||
- glog
|
||||
- React-jsinspector (= 0.60.5)
|
||||
- React-DevSupport (0.60.5):
|
||||
- React-Core (= 0.60.5)
|
||||
- React-RCTWebSocket (= 0.60.5)
|
||||
- React-jsi (0.60.5):
|
||||
- boost-for-react-native (= 1.63.0)
|
||||
- DoubleConversion
|
||||
- Folly (= 2018.10.22.00)
|
||||
- glog
|
||||
- React-jsi/Default (= 0.60.5)
|
||||
- React-jsi/Default (0.60.5):
|
||||
- boost-for-react-native (= 1.63.0)
|
||||
- DoubleConversion
|
||||
- Folly (= 2018.10.22.00)
|
||||
- glog
|
||||
- React-jsiexecutor (0.60.5):
|
||||
- DoubleConversion
|
||||
- Folly (= 2018.10.22.00)
|
||||
- glog
|
||||
- React-cxxreact (= 0.60.5)
|
||||
- React-jsi (= 0.60.5)
|
||||
- React-jsinspector (0.60.5)
|
||||
- react-native-webrtc (1.75.0):
|
||||
- React
|
||||
- React-RCTActionSheet (0.60.5):
|
||||
- React-Core (= 0.60.5)
|
||||
- React-RCTAnimation (0.60.5):
|
||||
- React-Core (= 0.60.5)
|
||||
- React-RCTBlob (0.60.5):
|
||||
- React-Core (= 0.60.5)
|
||||
- React-RCTNetwork (= 0.60.5)
|
||||
- React-RCTWebSocket (= 0.60.5)
|
||||
- React-RCTImage (0.60.5):
|
||||
- React-Core (= 0.60.5)
|
||||
- React-RCTNetwork (= 0.60.5)
|
||||
- React-RCTLinking (0.60.5):
|
||||
- React-Core (= 0.60.5)
|
||||
- React-RCTNetwork (0.60.5):
|
||||
- React-Core (= 0.60.5)
|
||||
- React-RCTSettings (0.60.5):
|
||||
- React-Core (= 0.60.5)
|
||||
- React-RCTText (0.60.5):
|
||||
- React-Core (= 0.60.5)
|
||||
- React-RCTVibration (0.60.5):
|
||||
- React-Core (= 0.60.5)
|
||||
- React-RCTWebSocket (0.60.5):
|
||||
- React-Core (= 0.60.5)
|
||||
- yoga (0.60.5.React)
|
||||
|
||||
DEPENDENCIES:
|
||||
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
|
||||
- Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)
|
||||
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
|
||||
- React (from `../node_modules/react-native/`)
|
||||
- React-Core (from `../node_modules/react-native/React`)
|
||||
- React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`)
|
||||
- React-DevSupport (from `../node_modules/react-native/React`)
|
||||
- React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
|
||||
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
|
||||
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
|
||||
- react-native-webrtc (from `../node_modules/openvidu-browser/node_modules/react-native-webrtc`)
|
||||
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
|
||||
- React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`)
|
||||
- React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`)
|
||||
- React-RCTImage (from `../node_modules/react-native/Libraries/Image`)
|
||||
- React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`)
|
||||
- React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`)
|
||||
- React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`)
|
||||
- React-RCTText (from `../node_modules/react-native/Libraries/Text`)
|
||||
- React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
|
||||
- React-RCTWebSocket (from `../node_modules/react-native/Libraries/WebSocket`)
|
||||
- yoga (from `../node_modules/react-native/ReactCommon/yoga`)
|
||||
|
||||
SPEC REPOS:
|
||||
https://github.com/cocoapods/specs.git:
|
||||
- boost-for-react-native
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
DoubleConversion:
|
||||
:podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
|
||||
Folly:
|
||||
:podspec: "../node_modules/react-native/third-party-podspecs/Folly.podspec"
|
||||
glog:
|
||||
:podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
|
||||
React:
|
||||
:path: "../node_modules/react-native/"
|
||||
React-Core:
|
||||
:path: "../node_modules/react-native/React"
|
||||
React-cxxreact:
|
||||
:path: "../node_modules/react-native/ReactCommon/cxxreact"
|
||||
React-DevSupport:
|
||||
:path: "../node_modules/react-native/React"
|
||||
React-jsi:
|
||||
:path: "../node_modules/react-native/ReactCommon/jsi"
|
||||
React-jsiexecutor:
|
||||
:path: "../node_modules/react-native/ReactCommon/jsiexecutor"
|
||||
React-jsinspector:
|
||||
:path: "../node_modules/react-native/ReactCommon/jsinspector"
|
||||
react-native-webrtc:
|
||||
:path: "../node_modules/openvidu-browser/node_modules/react-native-webrtc"
|
||||
React-RCTActionSheet:
|
||||
:path: "../node_modules/react-native/Libraries/ActionSheetIOS"
|
||||
React-RCTAnimation:
|
||||
:path: "../node_modules/react-native/Libraries/NativeAnimation"
|
||||
React-RCTBlob:
|
||||
:path: "../node_modules/react-native/Libraries/Blob"
|
||||
React-RCTImage:
|
||||
:path: "../node_modules/react-native/Libraries/Image"
|
||||
React-RCTLinking:
|
||||
:path: "../node_modules/react-native/Libraries/LinkingIOS"
|
||||
React-RCTNetwork:
|
||||
:path: "../node_modules/react-native/Libraries/Network"
|
||||
React-RCTSettings:
|
||||
:path: "../node_modules/react-native/Libraries/Settings"
|
||||
React-RCTText:
|
||||
:path: "../node_modules/react-native/Libraries/Text"
|
||||
React-RCTVibration:
|
||||
:path: "../node_modules/react-native/Libraries/Vibration"
|
||||
React-RCTWebSocket:
|
||||
:path: "../node_modules/react-native/Libraries/WebSocket"
|
||||
yoga:
|
||||
:path: "../node_modules/react-native/ReactCommon/yoga"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
|
||||
DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2
|
||||
Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51
|
||||
glog: 1f3da668190260b06b429bb211bfbee5cd790c28
|
||||
React: 53c53c4d99097af47cf60594b8706b4e3321e722
|
||||
React-Core: ba421f6b4f4cbe2fb17c0b6fc675f87622e78a64
|
||||
React-cxxreact: 8384287780c4999351ad9b6e7a149d9ed10a2395
|
||||
React-DevSupport: 197fb409737cff2c4f9986e77c220d7452cb9f9f
|
||||
React-jsi: 4d8c9efb6312a9725b18d6fc818ffc103f60fec2
|
||||
React-jsiexecutor: 90ad2f9db09513fc763bc757fdc3c4ff8bde2a30
|
||||
React-jsinspector: e08662d1bf5b129a3d556eb9ea343a3f40353ae4
|
||||
react-native-webrtc: c5e3d631179a933548a8e49bddbd8fad02586095
|
||||
React-RCTActionSheet: b0f1ea83f4bf75fb966eae9bfc47b78c8d3efd90
|
||||
React-RCTAnimation: 359ba1b5690b1e87cc173558a78e82d35919333e
|
||||
React-RCTBlob: 5e2b55f76e9a1c7ae52b826923502ddc3238df24
|
||||
React-RCTImage: f5f1c50922164e89bdda67bcd0153952a5cfe719
|
||||
React-RCTLinking: d0ecbd791e9ddddc41fa1f66b0255de90e8ee1e9
|
||||
React-RCTNetwork: e26946300b0ab7bb6c4a6348090e93fa21f33a9d
|
||||
React-RCTSettings: d0d37cb521b7470c998595a44f05847777cc3f42
|
||||
React-RCTText: b074d89033583d4f2eb5faf7ea2db3a13c7553a2
|
||||
React-RCTVibration: 2105b2e0e2b66a6408fc69a46c8a7fb5b2fdade0
|
||||
React-RCTWebSocket: cd932a16b7214898b6b7f788c8bddb3637246ac4
|
||||
yoga: 312528f5bbbba37b4dcea5ef00e8b4033fdd9411
|
||||
|
||||
PODFILE CHECKSUM: 61d6b92c08706e371b4bf4ecd88c7759f9ef5734
|
||||
|
||||
COCOAPODS: 1.7.4
|
||||
@ -1,26 +0,0 @@
|
||||
Copyright 2006-2011, the V8 project authors. All rights reserved.
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@ -1,54 +0,0 @@
|
||||
http://code.google.com/p/double-conversion
|
||||
|
||||
This project (double-conversion) provides binary-decimal and decimal-binary
|
||||
routines for IEEE doubles.
|
||||
|
||||
The library consists of efficient conversion routines that have been extracted
|
||||
from the V8 JavaScript engine. The code has been refactored and improved so that
|
||||
it can be used more easily in other projects.
|
||||
|
||||
There is extensive documentation in src/double-conversion.h. Other examples can
|
||||
be found in test/cctest/test-conversions.cc.
|
||||
|
||||
|
||||
Building
|
||||
========
|
||||
|
||||
This library can be built with scons [0] or cmake [1].
|
||||
The checked-in Makefile simply forwards to scons, and provides a
|
||||
shortcut to run all tests:
|
||||
|
||||
make
|
||||
make test
|
||||
|
||||
Scons
|
||||
-----
|
||||
|
||||
The easiest way to install this library is to use `scons`. It builds
|
||||
the static and shared library, and is set up to install those at the
|
||||
correct locations:
|
||||
|
||||
scons install
|
||||
|
||||
Use the `DESTDIR` option to change the target directory:
|
||||
|
||||
scons DESTDIR=alternative_directory install
|
||||
|
||||
Cmake
|
||||
-----
|
||||
|
||||
To use cmake run `cmake .` in the root directory. This overwrites the
|
||||
existing Makefile.
|
||||
|
||||
Use `-DBUILD_SHARED_LIBS=ON` to enable the compilation of shared libraries.
|
||||
Note that this disables static libraries. There is currently no way to
|
||||
build both libraries at the same time with cmake.
|
||||
|
||||
Use `-DBUILD_TESTING=ON` to build the test executable.
|
||||
|
||||
cmake . -DBUILD_TESTING=ON
|
||||
make
|
||||
test/cctest/cctest --list | tr -d '<' | xargs test/cctest/cctest
|
||||
|
||||
[0]: http://www.scons.org
|
||||
[1]: http://www.cmake.org
|
||||
@ -1,84 +0,0 @@
|
||||
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef DOUBLE_CONVERSION_BIGNUM_DTOA_H_
|
||||
#define DOUBLE_CONVERSION_BIGNUM_DTOA_H_
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
namespace double_conversion {
|
||||
|
||||
enum BignumDtoaMode {
|
||||
// Return the shortest correct representation.
|
||||
// For example the output of 0.299999999999999988897 is (the less accurate but
|
||||
// correct) 0.3.
|
||||
BIGNUM_DTOA_SHORTEST,
|
||||
// Same as BIGNUM_DTOA_SHORTEST but for single-precision floats.
|
||||
BIGNUM_DTOA_SHORTEST_SINGLE,
|
||||
// Return a fixed number of digits after the decimal point.
|
||||
// For instance fixed(0.1, 4) becomes 0.1000
|
||||
// If the input number is big, the output will be big.
|
||||
BIGNUM_DTOA_FIXED,
|
||||
// Return a fixed number of digits, no matter what the exponent is.
|
||||
BIGNUM_DTOA_PRECISION
|
||||
};
|
||||
|
||||
// Converts the given double 'v' to ascii.
|
||||
// The result should be interpreted as buffer * 10^(point-length).
|
||||
// The buffer will be null-terminated.
|
||||
//
|
||||
// The input v must be > 0 and different from NaN, and Infinity.
|
||||
//
|
||||
// The output depends on the given mode:
|
||||
// - SHORTEST: produce the least amount of digits for which the internal
|
||||
// identity requirement is still satisfied. If the digits are printed
|
||||
// (together with the correct exponent) then reading this number will give
|
||||
// 'v' again. The buffer will choose the representation that is closest to
|
||||
// 'v'. If there are two at the same distance, than the number is round up.
|
||||
// In this mode the 'requested_digits' parameter is ignored.
|
||||
// - FIXED: produces digits necessary to print a given number with
|
||||
// 'requested_digits' digits after the decimal point. The produced digits
|
||||
// might be too short in which case the caller has to fill the gaps with '0's.
|
||||
// Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2.
|
||||
// Halfway cases are rounded up. The call toFixed(0.15, 2) thus returns
|
||||
// buffer="2", point=0.
|
||||
// Note: the length of the returned buffer has no meaning wrt the significance
|
||||
// of its digits. That is, just because it contains '0's does not mean that
|
||||
// any other digit would not satisfy the internal identity requirement.
|
||||
// - PRECISION: produces 'requested_digits' where the first digit is not '0'.
|
||||
// Even though the length of produced digits usually equals
|
||||
// 'requested_digits', the function is allowed to return fewer digits, in
|
||||
// which case the caller has to fill the missing digits with '0's.
|
||||
// Halfway cases are again rounded up.
|
||||
// 'BignumDtoa' expects the given buffer to be big enough to hold all digits
|
||||
// and a terminating null-character.
|
||||
void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits,
|
||||
Vector<char> buffer, int* length, int* point);
|
||||
|
||||
} // namespace double_conversion
|
||||
|
||||
#endif // DOUBLE_CONVERSION_BIGNUM_DTOA_H_
|
||||
@ -1,145 +0,0 @@
|
||||
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef DOUBLE_CONVERSION_BIGNUM_H_
|
||||
#define DOUBLE_CONVERSION_BIGNUM_H_
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
namespace double_conversion {
|
||||
|
||||
class Bignum {
|
||||
public:
|
||||
// 3584 = 128 * 28. We can represent 2^3584 > 10^1000 accurately.
|
||||
// This bignum can encode much bigger numbers, since it contains an
|
||||
// exponent.
|
||||
static const int kMaxSignificantBits = 3584;
|
||||
|
||||
Bignum();
|
||||
void AssignUInt16(uint16_t value);
|
||||
void AssignUInt64(uint64_t value);
|
||||
void AssignBignum(const Bignum& other);
|
||||
|
||||
void AssignDecimalString(Vector<const char> value);
|
||||
void AssignHexString(Vector<const char> value);
|
||||
|
||||
void AssignPowerUInt16(uint16_t base, int exponent);
|
||||
|
||||
void AddUInt16(uint16_t operand);
|
||||
void AddUInt64(uint64_t operand);
|
||||
void AddBignum(const Bignum& other);
|
||||
// Precondition: this >= other.
|
||||
void SubtractBignum(const Bignum& other);
|
||||
|
||||
void Square();
|
||||
void ShiftLeft(int shift_amount);
|
||||
void MultiplyByUInt32(uint32_t factor);
|
||||
void MultiplyByUInt64(uint64_t factor);
|
||||
void MultiplyByPowerOfTen(int exponent);
|
||||
void Times10() { return MultiplyByUInt32(10); }
|
||||
// Pseudocode:
|
||||
// int result = this / other;
|
||||
// this = this % other;
|
||||
// In the worst case this function is in O(this/other).
|
||||
uint16_t DivideModuloIntBignum(const Bignum& other);
|
||||
|
||||
bool ToHexString(char* buffer, int buffer_size) const;
|
||||
|
||||
// Returns
|
||||
// -1 if a < b,
|
||||
// 0 if a == b, and
|
||||
// +1 if a > b.
|
||||
static int Compare(const Bignum& a, const Bignum& b);
|
||||
static bool Equal(const Bignum& a, const Bignum& b) {
|
||||
return Compare(a, b) == 0;
|
||||
}
|
||||
static bool LessEqual(const Bignum& a, const Bignum& b) {
|
||||
return Compare(a, b) <= 0;
|
||||
}
|
||||
static bool Less(const Bignum& a, const Bignum& b) {
|
||||
return Compare(a, b) < 0;
|
||||
}
|
||||
// Returns Compare(a + b, c);
|
||||
static int PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c);
|
||||
// Returns a + b == c
|
||||
static bool PlusEqual(const Bignum& a, const Bignum& b, const Bignum& c) {
|
||||
return PlusCompare(a, b, c) == 0;
|
||||
}
|
||||
// Returns a + b <= c
|
||||
static bool PlusLessEqual(const Bignum& a, const Bignum& b, const Bignum& c) {
|
||||
return PlusCompare(a, b, c) <= 0;
|
||||
}
|
||||
// Returns a + b < c
|
||||
static bool PlusLess(const Bignum& a, const Bignum& b, const Bignum& c) {
|
||||
return PlusCompare(a, b, c) < 0;
|
||||
}
|
||||
private:
|
||||
typedef uint32_t Chunk;
|
||||
typedef uint64_t DoubleChunk;
|
||||
|
||||
static const int kChunkSize = sizeof(Chunk) * 8;
|
||||
static const int kDoubleChunkSize = sizeof(DoubleChunk) * 8;
|
||||
// With bigit size of 28 we loose some bits, but a double still fits easily
|
||||
// into two chunks, and more importantly we can use the Comba multiplication.
|
||||
static const int kBigitSize = 28;
|
||||
static const Chunk kBigitMask = (1 << kBigitSize) - 1;
|
||||
// Every instance allocates kBigitLength chunks on the stack. Bignums cannot
|
||||
// grow. There are no checks if the stack-allocated space is sufficient.
|
||||
static const int kBigitCapacity = kMaxSignificantBits / kBigitSize;
|
||||
|
||||
void EnsureCapacity(int size) {
|
||||
if (size > kBigitCapacity) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
void Align(const Bignum& other);
|
||||
void Clamp();
|
||||
bool IsClamped() const;
|
||||
void Zero();
|
||||
// Requires this to have enough capacity (no tests done).
|
||||
// Updates used_digits_ if necessary.
|
||||
// shift_amount must be < kBigitSize.
|
||||
void BigitsShiftLeft(int shift_amount);
|
||||
// BigitLength includes the "hidden" digits encoded in the exponent.
|
||||
int BigitLength() const { return used_digits_ + exponent_; }
|
||||
Chunk BigitAt(int index) const;
|
||||
void SubtractTimes(const Bignum& other, int factor);
|
||||
|
||||
Chunk bigits_buffer_[kBigitCapacity];
|
||||
// A vector backed by bigits_buffer_. This way accesses to the array are
|
||||
// checked for out-of-bounds errors.
|
||||
Vector<Chunk> bigits_;
|
||||
int used_digits_;
|
||||
// The Bignum's value equals value(bigits_) * 2^(exponent_ * kBigitSize).
|
||||
int exponent_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Bignum);
|
||||
};
|
||||
|
||||
} // namespace double_conversion
|
||||
|
||||
#endif // DOUBLE_CONVERSION_BIGNUM_H_
|
||||
@ -1,176 +0,0 @@
|
||||
// Copyright 2006-2008 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#include "cached-powers.h"
|
||||
|
||||
namespace double_conversion {
|
||||
|
||||
struct CachedPower {
|
||||
uint64_t significand;
|
||||
int16_t binary_exponent;
|
||||
int16_t decimal_exponent;
|
||||
};
|
||||
|
||||
static const CachedPower kCachedPowers[] = {
|
||||
{UINT64_2PART_C(0xfa8fd5a0, 081c0288), -1220, -348},
|
||||
{UINT64_2PART_C(0xbaaee17f, a23ebf76), -1193, -340},
|
||||
{UINT64_2PART_C(0x8b16fb20, 3055ac76), -1166, -332},
|
||||
{UINT64_2PART_C(0xcf42894a, 5dce35ea), -1140, -324},
|
||||
{UINT64_2PART_C(0x9a6bb0aa, 55653b2d), -1113, -316},
|
||||
{UINT64_2PART_C(0xe61acf03, 3d1a45df), -1087, -308},
|
||||
{UINT64_2PART_C(0xab70fe17, c79ac6ca), -1060, -300},
|
||||
{UINT64_2PART_C(0xff77b1fc, bebcdc4f), -1034, -292},
|
||||
{UINT64_2PART_C(0xbe5691ef, 416bd60c), -1007, -284},
|
||||
{UINT64_2PART_C(0x8dd01fad, 907ffc3c), -980, -276},
|
||||
{UINT64_2PART_C(0xd3515c28, 31559a83), -954, -268},
|
||||
{UINT64_2PART_C(0x9d71ac8f, ada6c9b5), -927, -260},
|
||||
{UINT64_2PART_C(0xea9c2277, 23ee8bcb), -901, -252},
|
||||
{UINT64_2PART_C(0xaecc4991, 4078536d), -874, -244},
|
||||
{UINT64_2PART_C(0x823c1279, 5db6ce57), -847, -236},
|
||||
{UINT64_2PART_C(0xc2109436, 4dfb5637), -821, -228},
|
||||
{UINT64_2PART_C(0x9096ea6f, 3848984f), -794, -220},
|
||||
{UINT64_2PART_C(0xd77485cb, 25823ac7), -768, -212},
|
||||
{UINT64_2PART_C(0xa086cfcd, 97bf97f4), -741, -204},
|
||||
{UINT64_2PART_C(0xef340a98, 172aace5), -715, -196},
|
||||
{UINT64_2PART_C(0xb23867fb, 2a35b28e), -688, -188},
|
||||
{UINT64_2PART_C(0x84c8d4df, d2c63f3b), -661, -180},
|
||||
{UINT64_2PART_C(0xc5dd4427, 1ad3cdba), -635, -172},
|
||||
{UINT64_2PART_C(0x936b9fce, bb25c996), -608, -164},
|
||||
{UINT64_2PART_C(0xdbac6c24, 7d62a584), -582, -156},
|
||||
{UINT64_2PART_C(0xa3ab6658, 0d5fdaf6), -555, -148},
|
||||
{UINT64_2PART_C(0xf3e2f893, dec3f126), -529, -140},
|
||||
{UINT64_2PART_C(0xb5b5ada8, aaff80b8), -502, -132},
|
||||
{UINT64_2PART_C(0x87625f05, 6c7c4a8b), -475, -124},
|
||||
{UINT64_2PART_C(0xc9bcff60, 34c13053), -449, -116},
|
||||
{UINT64_2PART_C(0x964e858c, 91ba2655), -422, -108},
|
||||
{UINT64_2PART_C(0xdff97724, 70297ebd), -396, -100},
|
||||
{UINT64_2PART_C(0xa6dfbd9f, b8e5b88f), -369, -92},
|
||||
{UINT64_2PART_C(0xf8a95fcf, 88747d94), -343, -84},
|
||||
{UINT64_2PART_C(0xb9447093, 8fa89bcf), -316, -76},
|
||||
{UINT64_2PART_C(0x8a08f0f8, bf0f156b), -289, -68},
|
||||
{UINT64_2PART_C(0xcdb02555, 653131b6), -263, -60},
|
||||
{UINT64_2PART_C(0x993fe2c6, d07b7fac), -236, -52},
|
||||
{UINT64_2PART_C(0xe45c10c4, 2a2b3b06), -210, -44},
|
||||
{UINT64_2PART_C(0xaa242499, 697392d3), -183, -36},
|
||||
{UINT64_2PART_C(0xfd87b5f2, 8300ca0e), -157, -28},
|
||||
{UINT64_2PART_C(0xbce50864, 92111aeb), -130, -20},
|
||||
{UINT64_2PART_C(0x8cbccc09, 6f5088cc), -103, -12},
|
||||
{UINT64_2PART_C(0xd1b71758, e219652c), -77, -4},
|
||||
{UINT64_2PART_C(0x9c400000, 00000000), -50, 4},
|
||||
{UINT64_2PART_C(0xe8d4a510, 00000000), -24, 12},
|
||||
{UINT64_2PART_C(0xad78ebc5, ac620000), 3, 20},
|
||||
{UINT64_2PART_C(0x813f3978, f8940984), 30, 28},
|
||||
{UINT64_2PART_C(0xc097ce7b, c90715b3), 56, 36},
|
||||
{UINT64_2PART_C(0x8f7e32ce, 7bea5c70), 83, 44},
|
||||
{UINT64_2PART_C(0xd5d238a4, abe98068), 109, 52},
|
||||
{UINT64_2PART_C(0x9f4f2726, 179a2245), 136, 60},
|
||||
{UINT64_2PART_C(0xed63a231, d4c4fb27), 162, 68},
|
||||
{UINT64_2PART_C(0xb0de6538, 8cc8ada8), 189, 76},
|
||||
{UINT64_2PART_C(0x83c7088e, 1aab65db), 216, 84},
|
||||
{UINT64_2PART_C(0xc45d1df9, 42711d9a), 242, 92},
|
||||
{UINT64_2PART_C(0x924d692c, a61be758), 269, 100},
|
||||
{UINT64_2PART_C(0xda01ee64, 1a708dea), 295, 108},
|
||||
{UINT64_2PART_C(0xa26da399, 9aef774a), 322, 116},
|
||||
{UINT64_2PART_C(0xf209787b, b47d6b85), 348, 124},
|
||||
{UINT64_2PART_C(0xb454e4a1, 79dd1877), 375, 132},
|
||||
{UINT64_2PART_C(0x865b8692, 5b9bc5c2), 402, 140},
|
||||
{UINT64_2PART_C(0xc83553c5, c8965d3d), 428, 148},
|
||||
{UINT64_2PART_C(0x952ab45c, fa97a0b3), 455, 156},
|
||||
{UINT64_2PART_C(0xde469fbd, 99a05fe3), 481, 164},
|
||||
{UINT64_2PART_C(0xa59bc234, db398c25), 508, 172},
|
||||
{UINT64_2PART_C(0xf6c69a72, a3989f5c), 534, 180},
|
||||
{UINT64_2PART_C(0xb7dcbf53, 54e9bece), 561, 188},
|
||||
{UINT64_2PART_C(0x88fcf317, f22241e2), 588, 196},
|
||||
{UINT64_2PART_C(0xcc20ce9b, d35c78a5), 614, 204},
|
||||
{UINT64_2PART_C(0x98165af3, 7b2153df), 641, 212},
|
||||
{UINT64_2PART_C(0xe2a0b5dc, 971f303a), 667, 220},
|
||||
{UINT64_2PART_C(0xa8d9d153, 5ce3b396), 694, 228},
|
||||
{UINT64_2PART_C(0xfb9b7cd9, a4a7443c), 720, 236},
|
||||
{UINT64_2PART_C(0xbb764c4c, a7a44410), 747, 244},
|
||||
{UINT64_2PART_C(0x8bab8eef, b6409c1a), 774, 252},
|
||||
{UINT64_2PART_C(0xd01fef10, a657842c), 800, 260},
|
||||
{UINT64_2PART_C(0x9b10a4e5, e9913129), 827, 268},
|
||||
{UINT64_2PART_C(0xe7109bfb, a19c0c9d), 853, 276},
|
||||
{UINT64_2PART_C(0xac2820d9, 623bf429), 880, 284},
|
||||
{UINT64_2PART_C(0x80444b5e, 7aa7cf85), 907, 292},
|
||||
{UINT64_2PART_C(0xbf21e440, 03acdd2d), 933, 300},
|
||||
{UINT64_2PART_C(0x8e679c2f, 5e44ff8f), 960, 308},
|
||||
{UINT64_2PART_C(0xd433179d, 9c8cb841), 986, 316},
|
||||
{UINT64_2PART_C(0x9e19db92, b4e31ba9), 1013, 324},
|
||||
{UINT64_2PART_C(0xeb96bf6e, badf77d9), 1039, 332},
|
||||
{UINT64_2PART_C(0xaf87023b, 9bf0ee6b), 1066, 340},
|
||||
};
|
||||
|
||||
static const int kCachedPowersLength = ARRAY_SIZE(kCachedPowers);
|
||||
static const int kCachedPowersOffset = 348; // -1 * the first decimal_exponent.
|
||||
static const double kD_1_LOG2_10 = 0.30102999566398114; // 1 / lg(10)
|
||||
// Difference between the decimal exponents in the table above.
|
||||
const int PowersOfTenCache::kDecimalExponentDistance = 8;
|
||||
const int PowersOfTenCache::kMinDecimalExponent = -348;
|
||||
const int PowersOfTenCache::kMaxDecimalExponent = 340;
|
||||
|
||||
void PowersOfTenCache::GetCachedPowerForBinaryExponentRange(
|
||||
int min_exponent,
|
||||
int max_exponent,
|
||||
DiyFp* power,
|
||||
int* decimal_exponent) {
|
||||
int kQ = DiyFp::kSignificandSize;
|
||||
double k = ceil((min_exponent + kQ - 1) * kD_1_LOG2_10);
|
||||
int foo = kCachedPowersOffset;
|
||||
int index =
|
||||
(foo + static_cast<int>(k) - 1) / kDecimalExponentDistance + 1;
|
||||
ASSERT(0 <= index && index < kCachedPowersLength);
|
||||
CachedPower cached_power = kCachedPowers[index];
|
||||
ASSERT(min_exponent <= cached_power.binary_exponent);
|
||||
(void) max_exponent; // Mark variable as used.
|
||||
ASSERT(cached_power.binary_exponent <= max_exponent);
|
||||
*decimal_exponent = cached_power.decimal_exponent;
|
||||
*power = DiyFp(cached_power.significand, cached_power.binary_exponent);
|
||||
}
|
||||
|
||||
|
||||
void PowersOfTenCache::GetCachedPowerForDecimalExponent(int requested_exponent,
|
||||
DiyFp* power,
|
||||
int* found_exponent) {
|
||||
ASSERT(kMinDecimalExponent <= requested_exponent);
|
||||
ASSERT(requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance);
|
||||
int index =
|
||||
(requested_exponent + kCachedPowersOffset) / kDecimalExponentDistance;
|
||||
CachedPower cached_power = kCachedPowers[index];
|
||||
*power = DiyFp(cached_power.significand, cached_power.binary_exponent);
|
||||
*found_exponent = cached_power.decimal_exponent;
|
||||
ASSERT(*found_exponent <= requested_exponent);
|
||||
ASSERT(requested_exponent < *found_exponent + kDecimalExponentDistance);
|
||||
}
|
||||
|
||||
} // namespace double_conversion
|
||||
@ -1,64 +0,0 @@
|
||||
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef DOUBLE_CONVERSION_CACHED_POWERS_H_
|
||||
#define DOUBLE_CONVERSION_CACHED_POWERS_H_
|
||||
|
||||
#include "diy-fp.h"
|
||||
|
||||
namespace double_conversion {
|
||||
|
||||
class PowersOfTenCache {
|
||||
public:
|
||||
|
||||
// Not all powers of ten are cached. The decimal exponent of two neighboring
|
||||
// cached numbers will differ by kDecimalExponentDistance.
|
||||
static const int kDecimalExponentDistance;
|
||||
|
||||
static const int kMinDecimalExponent;
|
||||
static const int kMaxDecimalExponent;
|
||||
|
||||
// Returns a cached power-of-ten with a binary exponent in the range
|
||||
// [min_exponent; max_exponent] (boundaries included).
|
||||
static void GetCachedPowerForBinaryExponentRange(int min_exponent,
|
||||
int max_exponent,
|
||||
DiyFp* power,
|
||||
int* decimal_exponent);
|
||||
|
||||
// Returns a cached power of ten x ~= 10^k such that
|
||||
// k <= decimal_exponent < k + kCachedPowersDecimalDistance.
|
||||
// The given decimal_exponent must satisfy
|
||||
// kMinDecimalExponent <= requested_exponent, and
|
||||
// requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance.
|
||||
static void GetCachedPowerForDecimalExponent(int requested_exponent,
|
||||
DiyFp* power,
|
||||
int* found_exponent);
|
||||
};
|
||||
|
||||
} // namespace double_conversion
|
||||
|
||||
#endif // DOUBLE_CONVERSION_CACHED_POWERS_H_
|
||||
@ -1,57 +0,0 @@
|
||||
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
#include "diy-fp.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace double_conversion {
|
||||
|
||||
void DiyFp::Multiply(const DiyFp& other) {
|
||||
// Simply "emulates" a 128 bit multiplication.
|
||||
// However: the resulting number only contains 64 bits. The least
|
||||
// significant 64 bits are only used for rounding the most significant 64
|
||||
// bits.
|
||||
const uint64_t kM32 = 0xFFFFFFFFU;
|
||||
uint64_t a = f_ >> 32;
|
||||
uint64_t b = f_ & kM32;
|
||||
uint64_t c = other.f_ >> 32;
|
||||
uint64_t d = other.f_ & kM32;
|
||||
uint64_t ac = a * c;
|
||||
uint64_t bc = b * c;
|
||||
uint64_t ad = a * d;
|
||||
uint64_t bd = b * d;
|
||||
uint64_t tmp = (bd >> 32) + (ad & kM32) + (bc & kM32);
|
||||
// By adding 1U << 31 to tmp we round the final result.
|
||||
// Halfway cases will be round up.
|
||||
tmp += 1U << 31;
|
||||
uint64_t result_f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32);
|
||||
e_ += other.e_ + 64;
|
||||
f_ = result_f;
|
||||
}
|
||||
|
||||
} // namespace double_conversion
|
||||
@ -1,118 +0,0 @@
|
||||
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef DOUBLE_CONVERSION_DIY_FP_H_
|
||||
#define DOUBLE_CONVERSION_DIY_FP_H_
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
namespace double_conversion {
|
||||
|
||||
// This "Do It Yourself Floating Point" class implements a floating-point number
|
||||
// with a uint64 significand and an int exponent. Normalized DiyFp numbers will
|
||||
// have the most significant bit of the significand set.
|
||||
// Multiplication and Subtraction do not normalize their results.
|
||||
// DiyFp are not designed to contain special doubles (NaN and Infinity).
|
||||
class DiyFp {
|
||||
public:
|
||||
static const int kSignificandSize = 64;
|
||||
|
||||
DiyFp() : f_(0), e_(0) {}
|
||||
DiyFp(uint64_t f, int e) : f_(f), e_(e) {}
|
||||
|
||||
// this = this - other.
|
||||
// The exponents of both numbers must be the same and the significand of this
|
||||
// must be bigger than the significand of other.
|
||||
// The result will not be normalized.
|
||||
void Subtract(const DiyFp& other) {
|
||||
ASSERT(e_ == other.e_);
|
||||
ASSERT(f_ >= other.f_);
|
||||
f_ -= other.f_;
|
||||
}
|
||||
|
||||
// Returns a - b.
|
||||
// The exponents of both numbers must be the same and this must be bigger
|
||||
// than other. The result will not be normalized.
|
||||
static DiyFp Minus(const DiyFp& a, const DiyFp& b) {
|
||||
DiyFp result = a;
|
||||
result.Subtract(b);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// this = this * other.
|
||||
void Multiply(const DiyFp& other);
|
||||
|
||||
// returns a * b;
|
||||
static DiyFp Times(const DiyFp& a, const DiyFp& b) {
|
||||
DiyFp result = a;
|
||||
result.Multiply(b);
|
||||
return result;
|
||||
}
|
||||
|
||||
void Normalize() {
|
||||
ASSERT(f_ != 0);
|
||||
uint64_t f = f_;
|
||||
int e = e_;
|
||||
|
||||
// This method is mainly called for normalizing boundaries. In general
|
||||
// boundaries need to be shifted by 10 bits. We thus optimize for this case.
|
||||
const uint64_t k10MSBits = UINT64_2PART_C(0xFFC00000, 00000000);
|
||||
while ((f & k10MSBits) == 0) {
|
||||
f <<= 10;
|
||||
e -= 10;
|
||||
}
|
||||
while ((f & kUint64MSB) == 0) {
|
||||
f <<= 1;
|
||||
e--;
|
||||
}
|
||||
f_ = f;
|
||||
e_ = e;
|
||||
}
|
||||
|
||||
static DiyFp Normalize(const DiyFp& a) {
|
||||
DiyFp result = a;
|
||||
result.Normalize();
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t f() const { return f_; }
|
||||
int e() const { return e_; }
|
||||
|
||||
void set_f(uint64_t new_value) { f_ = new_value; }
|
||||
void set_e(int new_value) { e_ = new_value; }
|
||||
|
||||
private:
|
||||
static const uint64_t kUint64MSB = UINT64_2PART_C(0x80000000, 00000000);
|
||||
|
||||
uint64_t f_;
|
||||
int e_;
|
||||
};
|
||||
|
||||
} // namespace double_conversion
|
||||
|
||||
#endif // DOUBLE_CONVERSION_DIY_FP_H_
|
||||
@ -1,88 +0,0 @@
|
||||
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef DOUBLE_CONVERSION_FAST_DTOA_H_
|
||||
#define DOUBLE_CONVERSION_FAST_DTOA_H_
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
namespace double_conversion {
|
||||
|
||||
enum FastDtoaMode {
|
||||
// Computes the shortest representation of the given input. The returned
|
||||
// result will be the most accurate number of this length. Longer
|
||||
// representations might be more accurate.
|
||||
FAST_DTOA_SHORTEST,
|
||||
// Same as FAST_DTOA_SHORTEST but for single-precision floats.
|
||||
FAST_DTOA_SHORTEST_SINGLE,
|
||||
// Computes a representation where the precision (number of digits) is
|
||||
// given as input. The precision is independent of the decimal point.
|
||||
FAST_DTOA_PRECISION
|
||||
};
|
||||
|
||||
// FastDtoa will produce at most kFastDtoaMaximalLength digits. This does not
|
||||
// include the terminating '\0' character.
|
||||
static const int kFastDtoaMaximalLength = 17;
|
||||
// Same for single-precision numbers.
|
||||
static const int kFastDtoaMaximalSingleLength = 9;
|
||||
|
||||
// Provides a decimal representation of v.
|
||||
// The result should be interpreted as buffer * 10^(point - length).
|
||||
//
|
||||
// Precondition:
|
||||
// * v must be a strictly positive finite double.
|
||||
//
|
||||
// Returns true if it succeeds, otherwise the result can not be trusted.
|
||||
// There will be *length digits inside the buffer followed by a null terminator.
|
||||
// If the function returns true and mode equals
|
||||
// - FAST_DTOA_SHORTEST, then
|
||||
// the parameter requested_digits is ignored.
|
||||
// The result satisfies
|
||||
// v == (double) (buffer * 10^(point - length)).
|
||||
// The digits in the buffer are the shortest representation possible. E.g.
|
||||
// if 0.099999999999 and 0.1 represent the same double then "1" is returned
|
||||
// with point = 0.
|
||||
// The last digit will be closest to the actual v. That is, even if several
|
||||
// digits might correctly yield 'v' when read again, the buffer will contain
|
||||
// the one closest to v.
|
||||
// - FAST_DTOA_PRECISION, then
|
||||
// the buffer contains requested_digits digits.
|
||||
// the difference v - (buffer * 10^(point-length)) is closest to zero for
|
||||
// all possible representations of requested_digits digits.
|
||||
// If there are two values that are equally close, then FastDtoa returns
|
||||
// false.
|
||||
// For both modes the buffer must be large enough to hold the result.
|
||||
bool FastDtoa(double d,
|
||||
FastDtoaMode mode,
|
||||
int requested_digits,
|
||||
Vector<char> buffer,
|
||||
int* length,
|
||||
int* decimal_point);
|
||||
|
||||
} // namespace double_conversion
|
||||
|
||||
#endif // DOUBLE_CONVERSION_FAST_DTOA_H_
|
||||
@ -1,404 +0,0 @@
|
||||
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "fixed-dtoa.h"
|
||||
#include "ieee.h"
|
||||
|
||||
namespace double_conversion {
|
||||
|
||||
// Represents a 128bit type. This class should be replaced by a native type on
|
||||
// platforms that support 128bit integers.
|
||||
class UInt128 {
|
||||
public:
|
||||
UInt128() : high_bits_(0), low_bits_(0) { }
|
||||
UInt128(uint64_t high, uint64_t low) : high_bits_(high), low_bits_(low) { }
|
||||
|
||||
void Multiply(uint32_t multiplicand) {
|
||||
uint64_t accumulator;
|
||||
|
||||
accumulator = (low_bits_ & kMask32) * multiplicand;
|
||||
uint32_t part = static_cast<uint32_t>(accumulator & kMask32);
|
||||
accumulator >>= 32;
|
||||
accumulator = accumulator + (low_bits_ >> 32) * multiplicand;
|
||||
low_bits_ = (accumulator << 32) + part;
|
||||
accumulator >>= 32;
|
||||
accumulator = accumulator + (high_bits_ & kMask32) * multiplicand;
|
||||
part = static_cast<uint32_t>(accumulator & kMask32);
|
||||
accumulator >>= 32;
|
||||
accumulator = accumulator + (high_bits_ >> 32) * multiplicand;
|
||||
high_bits_ = (accumulator << 32) + part;
|
||||
ASSERT((accumulator >> 32) == 0);
|
||||
}
|
||||
|
||||
void Shift(int shift_amount) {
|
||||
ASSERT(-64 <= shift_amount && shift_amount <= 64);
|
||||
if (shift_amount == 0) {
|
||||
return;
|
||||
} else if (shift_amount == -64) {
|
||||
high_bits_ = low_bits_;
|
||||
low_bits_ = 0;
|
||||
} else if (shift_amount == 64) {
|
||||
low_bits_ = high_bits_;
|
||||
high_bits_ = 0;
|
||||
} else if (shift_amount <= 0) {
|
||||
high_bits_ <<= -shift_amount;
|
||||
high_bits_ += low_bits_ >> (64 + shift_amount);
|
||||
low_bits_ <<= -shift_amount;
|
||||
} else {
|
||||
low_bits_ >>= shift_amount;
|
||||
low_bits_ += high_bits_ << (64 - shift_amount);
|
||||
high_bits_ >>= shift_amount;
|
||||
}
|
||||
}
|
||||
|
||||
// Modifies *this to *this MOD (2^power).
|
||||
// Returns *this DIV (2^power).
|
||||
int DivModPowerOf2(int power) {
|
||||
if (power >= 64) {
|
||||
int result = static_cast<int>(high_bits_ >> (power - 64));
|
||||
high_bits_ -= static_cast<uint64_t>(result) << (power - 64);
|
||||
return result;
|
||||
} else {
|
||||
uint64_t part_low = low_bits_ >> power;
|
||||
uint64_t part_high = high_bits_ << (64 - power);
|
||||
int result = static_cast<int>(part_low + part_high);
|
||||
high_bits_ = 0;
|
||||
low_bits_ -= part_low << power;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsZero() const {
|
||||
return high_bits_ == 0 && low_bits_ == 0;
|
||||
}
|
||||
|
||||
int BitAt(int position) {
|
||||
if (position >= 64) {
|
||||
return static_cast<int>(high_bits_ >> (position - 64)) & 1;
|
||||
} else {
|
||||
return static_cast<int>(low_bits_ >> position) & 1;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static const uint64_t kMask32 = 0xFFFFFFFF;
|
||||
// Value == (high_bits_ << 64) + low_bits_
|
||||
uint64_t high_bits_;
|
||||
uint64_t low_bits_;
|
||||
};
|
||||
|
||||
|
||||
static const int kDoubleSignificandSize = 53; // Includes the hidden bit.
|
||||
|
||||
|
||||
static void FillDigits32FixedLength(uint32_t number, int requested_length,
|
||||
Vector<char> buffer, int* length) {
|
||||
for (int i = requested_length - 1; i >= 0; --i) {
|
||||
buffer[(*length) + i] = '0' + number % 10;
|
||||
number /= 10;
|
||||
}
|
||||
*length += requested_length;
|
||||
}
|
||||
|
||||
|
||||
static void FillDigits32(uint32_t number, Vector<char> buffer, int* length) {
|
||||
int number_length = 0;
|
||||
// We fill the digits in reverse order and exchange them afterwards.
|
||||
while (number != 0) {
|
||||
int digit = number % 10;
|
||||
number /= 10;
|
||||
buffer[(*length) + number_length] = static_cast<char>('0' + digit);
|
||||
number_length++;
|
||||
}
|
||||
// Exchange the digits.
|
||||
int i = *length;
|
||||
int j = *length + number_length - 1;
|
||||
while (i < j) {
|
||||
char tmp = buffer[i];
|
||||
buffer[i] = buffer[j];
|
||||
buffer[j] = tmp;
|
||||
i++;
|
||||
j--;
|
||||
}
|
||||
*length += number_length;
|
||||
}
|
||||
|
||||
|
||||
static void FillDigits64FixedLength(uint64_t number,
|
||||
Vector<char> buffer, int* length) {
|
||||
const uint32_t kTen7 = 10000000;
|
||||
// For efficiency cut the number into 3 uint32_t parts, and print those.
|
||||
uint32_t part2 = static_cast<uint32_t>(number % kTen7);
|
||||
number /= kTen7;
|
||||
uint32_t part1 = static_cast<uint32_t>(number % kTen7);
|
||||
uint32_t part0 = static_cast<uint32_t>(number / kTen7);
|
||||
|
||||
FillDigits32FixedLength(part0, 3, buffer, length);
|
||||
FillDigits32FixedLength(part1, 7, buffer, length);
|
||||
FillDigits32FixedLength(part2, 7, buffer, length);
|
||||
}
|
||||
|
||||
|
||||
static void FillDigits64(uint64_t number, Vector<char> buffer, int* length) {
|
||||
const uint32_t kTen7 = 10000000;
|
||||
// For efficiency cut the number into 3 uint32_t parts, and print those.
|
||||
uint32_t part2 = static_cast<uint32_t>(number % kTen7);
|
||||
number /= kTen7;
|
||||
uint32_t part1 = static_cast<uint32_t>(number % kTen7);
|
||||
uint32_t part0 = static_cast<uint32_t>(number / kTen7);
|
||||
|
||||
if (part0 != 0) {
|
||||
FillDigits32(part0, buffer, length);
|
||||
FillDigits32FixedLength(part1, 7, buffer, length);
|
||||
FillDigits32FixedLength(part2, 7, buffer, length);
|
||||
} else if (part1 != 0) {
|
||||
FillDigits32(part1, buffer, length);
|
||||
FillDigits32FixedLength(part2, 7, buffer, length);
|
||||
} else {
|
||||
FillDigits32(part2, buffer, length);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void RoundUp(Vector<char> buffer, int* length, int* decimal_point) {
|
||||
// An empty buffer represents 0.
|
||||
if (*length == 0) {
|
||||
buffer[0] = '1';
|
||||
*decimal_point = 1;
|
||||
*length = 1;
|
||||
return;
|
||||
}
|
||||
// Round the last digit until we either have a digit that was not '9' or until
|
||||
// we reached the first digit.
|
||||
buffer[(*length) - 1]++;
|
||||
for (int i = (*length) - 1; i > 0; --i) {
|
||||
if (buffer[i] != '0' + 10) {
|
||||
return;
|
||||
}
|
||||
buffer[i] = '0';
|
||||
buffer[i - 1]++;
|
||||
}
|
||||
// If the first digit is now '0' + 10, we would need to set it to '0' and add
|
||||
// a '1' in front. However we reach the first digit only if all following
|
||||
// digits had been '9' before rounding up. Now all trailing digits are '0' and
|
||||
// we simply switch the first digit to '1' and update the decimal-point
|
||||
// (indicating that the point is now one digit to the right).
|
||||
if (buffer[0] == '0' + 10) {
|
||||
buffer[0] = '1';
|
||||
(*decimal_point)++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The given fractionals number represents a fixed-point number with binary
|
||||
// point at bit (-exponent).
|
||||
// Preconditions:
|
||||
// -128 <= exponent <= 0.
|
||||
// 0 <= fractionals * 2^exponent < 1
|
||||
// The buffer holds the result.
|
||||
// The function will round its result. During the rounding-process digits not
|
||||
// generated by this function might be updated, and the decimal-point variable
|
||||
// might be updated. If this function generates the digits 99 and the buffer
|
||||
// already contained "199" (thus yielding a buffer of "19999") then a
|
||||
// rounding-up will change the contents of the buffer to "20000".
|
||||
static void FillFractionals(uint64_t fractionals, int exponent,
|
||||
int fractional_count, Vector<char> buffer,
|
||||
int* length, int* decimal_point) {
|
||||
ASSERT(-128 <= exponent && exponent <= 0);
|
||||
// 'fractionals' is a fixed-point number, with binary point at bit
|
||||
// (-exponent). Inside the function the non-converted remainder of fractionals
|
||||
// is a fixed-point number, with binary point at bit 'point'.
|
||||
if (-exponent <= 64) {
|
||||
// One 64 bit number is sufficient.
|
||||
ASSERT(fractionals >> 56 == 0);
|
||||
int point = -exponent;
|
||||
for (int i = 0; i < fractional_count; ++i) {
|
||||
if (fractionals == 0) break;
|
||||
// Instead of multiplying by 10 we multiply by 5 and adjust the point
|
||||
// location. This way the fractionals variable will not overflow.
|
||||
// Invariant at the beginning of the loop: fractionals < 2^point.
|
||||
// Initially we have: point <= 64 and fractionals < 2^56
|
||||
// After each iteration the point is decremented by one.
|
||||
// Note that 5^3 = 125 < 128 = 2^7.
|
||||
// Therefore three iterations of this loop will not overflow fractionals
|
||||
// (even without the subtraction at the end of the loop body). At this
|
||||
// time point will satisfy point <= 61 and therefore fractionals < 2^point
|
||||
// and any further multiplication of fractionals by 5 will not overflow.
|
||||
fractionals *= 5;
|
||||
point--;
|
||||
int digit = static_cast<int>(fractionals >> point);
|
||||
ASSERT(digit <= 9);
|
||||
buffer[*length] = static_cast<char>('0' + digit);
|
||||
(*length)++;
|
||||
fractionals -= static_cast<uint64_t>(digit) << point;
|
||||
}
|
||||
// If the first bit after the point is set we have to round up.
|
||||
if (((fractionals >> (point - 1)) & 1) == 1) {
|
||||
RoundUp(buffer, length, decimal_point);
|
||||
}
|
||||
} else { // We need 128 bits.
|
||||
ASSERT(64 < -exponent && -exponent <= 128);
|
||||
UInt128 fractionals128 = UInt128(fractionals, 0);
|
||||
fractionals128.Shift(-exponent - 64);
|
||||
int point = 128;
|
||||
for (int i = 0; i < fractional_count; ++i) {
|
||||
if (fractionals128.IsZero()) break;
|
||||
// As before: instead of multiplying by 10 we multiply by 5 and adjust the
|
||||
// point location.
|
||||
// This multiplication will not overflow for the same reasons as before.
|
||||
fractionals128.Multiply(5);
|
||||
point--;
|
||||
int digit = fractionals128.DivModPowerOf2(point);
|
||||
ASSERT(digit <= 9);
|
||||
buffer[*length] = static_cast<char>('0' + digit);
|
||||
(*length)++;
|
||||
}
|
||||
if (fractionals128.BitAt(point - 1) == 1) {
|
||||
RoundUp(buffer, length, decimal_point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Removes leading and trailing zeros.
|
||||
// If leading zeros are removed then the decimal point position is adjusted.
|
||||
static void TrimZeros(Vector<char> buffer, int* length, int* decimal_point) {
|
||||
while (*length > 0 && buffer[(*length) - 1] == '0') {
|
||||
(*length)--;
|
||||
}
|
||||
int first_non_zero = 0;
|
||||
while (first_non_zero < *length && buffer[first_non_zero] == '0') {
|
||||
first_non_zero++;
|
||||
}
|
||||
if (first_non_zero != 0) {
|
||||
for (int i = first_non_zero; i < *length; ++i) {
|
||||
buffer[i - first_non_zero] = buffer[i];
|
||||
}
|
||||
*length -= first_non_zero;
|
||||
*decimal_point -= first_non_zero;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool FastFixedDtoa(double v,
|
||||
int fractional_count,
|
||||
Vector<char> buffer,
|
||||
int* length,
|
||||
int* decimal_point) {
|
||||
const uint32_t kMaxUInt32 = 0xFFFFFFFF;
|
||||
uint64_t significand = Double(v).Significand();
|
||||
int exponent = Double(v).Exponent();
|
||||
// v = significand * 2^exponent (with significand a 53bit integer).
|
||||
// If the exponent is larger than 20 (i.e. we may have a 73bit number) then we
|
||||
// don't know how to compute the representation. 2^73 ~= 9.5*10^21.
|
||||
// If necessary this limit could probably be increased, but we don't need
|
||||
// more.
|
||||
if (exponent > 20) return false;
|
||||
if (fractional_count > 20) return false;
|
||||
*length = 0;
|
||||
// At most kDoubleSignificandSize bits of the significand are non-zero.
|
||||
// Given a 64 bit integer we have 11 0s followed by 53 potentially non-zero
|
||||
// bits: 0..11*..0xxx..53*..xx
|
||||
if (exponent + kDoubleSignificandSize > 64) {
|
||||
// The exponent must be > 11.
|
||||
//
|
||||
// We know that v = significand * 2^exponent.
|
||||
// And the exponent > 11.
|
||||
// We simplify the task by dividing v by 10^17.
|
||||
// The quotient delivers the first digits, and the remainder fits into a 64
|
||||
// bit number.
|
||||
// Dividing by 10^17 is equivalent to dividing by 5^17*2^17.
|
||||
const uint64_t kFive17 = UINT64_2PART_C(0xB1, A2BC2EC5); // 5^17
|
||||
uint64_t divisor = kFive17;
|
||||
int divisor_power = 17;
|
||||
uint64_t dividend = significand;
|
||||
uint32_t quotient;
|
||||
uint64_t remainder;
|
||||
// Let v = f * 2^e with f == significand and e == exponent.
|
||||
// Then need q (quotient) and r (remainder) as follows:
|
||||
// v = q * 10^17 + r
|
||||
// f * 2^e = q * 10^17 + r
|
||||
// f * 2^e = q * 5^17 * 2^17 + r
|
||||
// If e > 17 then
|
||||
// f * 2^(e-17) = q * 5^17 + r/2^17
|
||||
// else
|
||||
// f = q * 5^17 * 2^(17-e) + r/2^e
|
||||
if (exponent > divisor_power) {
|
||||
// We only allow exponents of up to 20 and therefore (17 - e) <= 3
|
||||
dividend <<= exponent - divisor_power;
|
||||
quotient = static_cast<uint32_t>(dividend / divisor);
|
||||
remainder = (dividend % divisor) << divisor_power;
|
||||
} else {
|
||||
divisor <<= divisor_power - exponent;
|
||||
quotient = static_cast<uint32_t>(dividend / divisor);
|
||||
remainder = (dividend % divisor) << exponent;
|
||||
}
|
||||
FillDigits32(quotient, buffer, length);
|
||||
FillDigits64FixedLength(remainder, buffer, length);
|
||||
*decimal_point = *length;
|
||||
} else if (exponent >= 0) {
|
||||
// 0 <= exponent <= 11
|
||||
significand <<= exponent;
|
||||
FillDigits64(significand, buffer, length);
|
||||
*decimal_point = *length;
|
||||
} else if (exponent > -kDoubleSignificandSize) {
|
||||
// We have to cut the number.
|
||||
uint64_t integrals = significand >> -exponent;
|
||||
uint64_t fractionals = significand - (integrals << -exponent);
|
||||
if (integrals > kMaxUInt32) {
|
||||
FillDigits64(integrals, buffer, length);
|
||||
} else {
|
||||
FillDigits32(static_cast<uint32_t>(integrals), buffer, length);
|
||||
}
|
||||
*decimal_point = *length;
|
||||
FillFractionals(fractionals, exponent, fractional_count,
|
||||
buffer, length, decimal_point);
|
||||
} else if (exponent < -128) {
|
||||
// This configuration (with at most 20 digits) means that all digits must be
|
||||
// 0.
|
||||
ASSERT(fractional_count <= 20);
|
||||
buffer[0] = '\0';
|
||||
*length = 0;
|
||||
*decimal_point = -fractional_count;
|
||||
} else {
|
||||
*decimal_point = 0;
|
||||
FillFractionals(significand, exponent, fractional_count,
|
||||
buffer, length, decimal_point);
|
||||
}
|
||||
TrimZeros(buffer, length, decimal_point);
|
||||
buffer[*length] = '\0';
|
||||
if ((*length) == 0) {
|
||||
// The string is empty and the decimal_point thus has no importance. Mimick
|
||||
// Gay's dtoa and and set it to -fractional_count.
|
||||
*decimal_point = -fractional_count;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace double_conversion
|
||||
@ -1,56 +0,0 @@
|
||||
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef DOUBLE_CONVERSION_FIXED_DTOA_H_
|
||||
#define DOUBLE_CONVERSION_FIXED_DTOA_H_
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
namespace double_conversion {
|
||||
|
||||
// Produces digits necessary to print a given number with
|
||||
// 'fractional_count' digits after the decimal point.
|
||||
// The buffer must be big enough to hold the result plus one terminating null
|
||||
// character.
|
||||
//
|
||||
// The produced digits might be too short in which case the caller has to fill
|
||||
// the gaps with '0's.
|
||||
// Example: FastFixedDtoa(0.001, 5, ...) is allowed to return buffer = "1", and
|
||||
// decimal_point = -2.
|
||||
// Halfway cases are rounded towards +/-Infinity (away from 0). The call
|
||||
// FastFixedDtoa(0.15, 2, ...) thus returns buffer = "2", decimal_point = 0.
|
||||
// The returned buffer may contain digits that would be truncated from the
|
||||
// shortest representation of the input.
|
||||
//
|
||||
// This method only works for some parameters. If it can't handle the input it
|
||||
// returns false. The output is null-terminated when the function succeeds.
|
||||
bool FastFixedDtoa(double v, int fractional_count,
|
||||
Vector<char> buffer, int* length, int* decimal_point);
|
||||
|
||||
} // namespace double_conversion
|
||||
|
||||
#endif // DOUBLE_CONVERSION_FIXED_DTOA_H_
|
||||
@ -1,402 +0,0 @@
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef DOUBLE_CONVERSION_DOUBLE_H_
|
||||
#define DOUBLE_CONVERSION_DOUBLE_H_
|
||||
|
||||
#include "diy-fp.h"
|
||||
|
||||
namespace double_conversion {
|
||||
|
||||
// We assume that doubles and uint64_t have the same endianness.
|
||||
static uint64_t double_to_uint64(double d) { return BitCast<uint64_t>(d); }
|
||||
static double uint64_to_double(uint64_t d64) { return BitCast<double>(d64); }
|
||||
static uint32_t float_to_uint32(float f) { return BitCast<uint32_t>(f); }
|
||||
static float uint32_to_float(uint32_t d32) { return BitCast<float>(d32); }
|
||||
|
||||
// Helper functions for doubles.
|
||||
class Double {
|
||||
public:
|
||||
static const uint64_t kSignMask = UINT64_2PART_C(0x80000000, 00000000);
|
||||
static const uint64_t kExponentMask = UINT64_2PART_C(0x7FF00000, 00000000);
|
||||
static const uint64_t kSignificandMask = UINT64_2PART_C(0x000FFFFF, FFFFFFFF);
|
||||
static const uint64_t kHiddenBit = UINT64_2PART_C(0x00100000, 00000000);
|
||||
static const int kPhysicalSignificandSize = 52; // Excludes the hidden bit.
|
||||
static const int kSignificandSize = 53;
|
||||
|
||||
Double() : d64_(0) {}
|
||||
explicit Double(double d) : d64_(double_to_uint64(d)) {}
|
||||
explicit Double(uint64_t d64) : d64_(d64) {}
|
||||
explicit Double(DiyFp diy_fp)
|
||||
: d64_(DiyFpToUint64(diy_fp)) {}
|
||||
|
||||
// The value encoded by this Double must be greater or equal to +0.0.
|
||||
// It must not be special (infinity, or NaN).
|
||||
DiyFp AsDiyFp() const {
|
||||
ASSERT(Sign() > 0);
|
||||
ASSERT(!IsSpecial());
|
||||
return DiyFp(Significand(), Exponent());
|
||||
}
|
||||
|
||||
// The value encoded by this Double must be strictly greater than 0.
|
||||
DiyFp AsNormalizedDiyFp() const {
|
||||
ASSERT(value() > 0.0);
|
||||
uint64_t f = Significand();
|
||||
int e = Exponent();
|
||||
|
||||
// The current double could be a denormal.
|
||||
while ((f & kHiddenBit) == 0) {
|
||||
f <<= 1;
|
||||
e--;
|
||||
}
|
||||
// Do the final shifts in one go.
|
||||
f <<= DiyFp::kSignificandSize - kSignificandSize;
|
||||
e -= DiyFp::kSignificandSize - kSignificandSize;
|
||||
return DiyFp(f, e);
|
||||
}
|
||||
|
||||
// Returns the double's bit as uint64.
|
||||
uint64_t AsUint64() const {
|
||||
return d64_;
|
||||
}
|
||||
|
||||
// Returns the next greater double. Returns +infinity on input +infinity.
|
||||
double NextDouble() const {
|
||||
if (d64_ == kInfinity) return Double(kInfinity).value();
|
||||
if (Sign() < 0 && Significand() == 0) {
|
||||
// -0.0
|
||||
return 0.0;
|
||||
}
|
||||
if (Sign() < 0) {
|
||||
return Double(d64_ - 1).value();
|
||||
} else {
|
||||
return Double(d64_ + 1).value();
|
||||
}
|
||||
}
|
||||
|
||||
double PreviousDouble() const {
|
||||
if (d64_ == (kInfinity | kSignMask)) return -Double::Infinity();
|
||||
if (Sign() < 0) {
|
||||
return Double(d64_ + 1).value();
|
||||
} else {
|
||||
if (Significand() == 0) return -0.0;
|
||||
return Double(d64_ - 1).value();
|
||||
}
|
||||
}
|
||||
|
||||
int Exponent() const {
|
||||
if (IsDenormal()) return kDenormalExponent;
|
||||
|
||||
uint64_t d64 = AsUint64();
|
||||
int biased_e =
|
||||
static_cast<int>((d64 & kExponentMask) >> kPhysicalSignificandSize);
|
||||
return biased_e - kExponentBias;
|
||||
}
|
||||
|
||||
uint64_t Significand() const {
|
||||
uint64_t d64 = AsUint64();
|
||||
uint64_t significand = d64 & kSignificandMask;
|
||||
if (!IsDenormal()) {
|
||||
return significand + kHiddenBit;
|
||||
} else {
|
||||
return significand;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if the double is a denormal.
|
||||
bool IsDenormal() const {
|
||||
uint64_t d64 = AsUint64();
|
||||
return (d64 & kExponentMask) == 0;
|
||||
}
|
||||
|
||||
// We consider denormals not to be special.
|
||||
// Hence only Infinity and NaN are special.
|
||||
bool IsSpecial() const {
|
||||
uint64_t d64 = AsUint64();
|
||||
return (d64 & kExponentMask) == kExponentMask;
|
||||
}
|
||||
|
||||
bool IsNan() const {
|
||||
uint64_t d64 = AsUint64();
|
||||
return ((d64 & kExponentMask) == kExponentMask) &&
|
||||
((d64 & kSignificandMask) != 0);
|
||||
}
|
||||
|
||||
bool IsInfinite() const {
|
||||
uint64_t d64 = AsUint64();
|
||||
return ((d64 & kExponentMask) == kExponentMask) &&
|
||||
((d64 & kSignificandMask) == 0);
|
||||
}
|
||||
|
||||
int Sign() const {
|
||||
uint64_t d64 = AsUint64();
|
||||
return (d64 & kSignMask) == 0? 1: -1;
|
||||
}
|
||||
|
||||
// Precondition: the value encoded by this Double must be greater or equal
|
||||
// than +0.0.
|
||||
DiyFp UpperBoundary() const {
|
||||
ASSERT(Sign() > 0);
|
||||
return DiyFp(Significand() * 2 + 1, Exponent() - 1);
|
||||
}
|
||||
|
||||
// Computes the two boundaries of this.
|
||||
// The bigger boundary (m_plus) is normalized. The lower boundary has the same
|
||||
// exponent as m_plus.
|
||||
// Precondition: the value encoded by this Double must be greater than 0.
|
||||
void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const {
|
||||
ASSERT(value() > 0.0);
|
||||
DiyFp v = this->AsDiyFp();
|
||||
DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1));
|
||||
DiyFp m_minus;
|
||||
if (LowerBoundaryIsCloser()) {
|
||||
m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2);
|
||||
} else {
|
||||
m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1);
|
||||
}
|
||||
m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e()));
|
||||
m_minus.set_e(m_plus.e());
|
||||
*out_m_plus = m_plus;
|
||||
*out_m_minus = m_minus;
|
||||
}
|
||||
|
||||
bool LowerBoundaryIsCloser() const {
|
||||
// The boundary is closer if the significand is of the form f == 2^p-1 then
|
||||
// the lower boundary is closer.
|
||||
// Think of v = 1000e10 and v- = 9999e9.
|
||||
// Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but
|
||||
// at a distance of 1e8.
|
||||
// The only exception is for the smallest normal: the largest denormal is
|
||||
// at the same distance as its successor.
|
||||
// Note: denormals have the same exponent as the smallest normals.
|
||||
bool physical_significand_is_zero = ((AsUint64() & kSignificandMask) == 0);
|
||||
return physical_significand_is_zero && (Exponent() != kDenormalExponent);
|
||||
}
|
||||
|
||||
double value() const { return uint64_to_double(d64_); }
|
||||
|
||||
// Returns the significand size for a given order of magnitude.
|
||||
// If v = f*2^e with 2^p-1 <= f <= 2^p then p+e is v's order of magnitude.
|
||||
// This function returns the number of significant binary digits v will have
|
||||
// once it's encoded into a double. In almost all cases this is equal to
|
||||
// kSignificandSize. The only exceptions are denormals. They start with
|
||||
// leading zeroes and their effective significand-size is hence smaller.
|
||||
static int SignificandSizeForOrderOfMagnitude(int order) {
|
||||
if (order >= (kDenormalExponent + kSignificandSize)) {
|
||||
return kSignificandSize;
|
||||
}
|
||||
if (order <= kDenormalExponent) return 0;
|
||||
return order - kDenormalExponent;
|
||||
}
|
||||
|
||||
static double Infinity() {
|
||||
return Double(kInfinity).value();
|
||||
}
|
||||
|
||||
static double NaN() {
|
||||
return Double(kNaN).value();
|
||||
}
|
||||
|
||||
private:
|
||||
static const int kExponentBias = 0x3FF + kPhysicalSignificandSize;
|
||||
static const int kDenormalExponent = -kExponentBias + 1;
|
||||
static const int kMaxExponent = 0x7FF - kExponentBias;
|
||||
static const uint64_t kInfinity = UINT64_2PART_C(0x7FF00000, 00000000);
|
||||
static const uint64_t kNaN = UINT64_2PART_C(0x7FF80000, 00000000);
|
||||
|
||||
const uint64_t d64_;
|
||||
|
||||
static uint64_t DiyFpToUint64(DiyFp diy_fp) {
|
||||
uint64_t significand = diy_fp.f();
|
||||
int exponent = diy_fp.e();
|
||||
while (significand > kHiddenBit + kSignificandMask) {
|
||||
significand >>= 1;
|
||||
exponent++;
|
||||
}
|
||||
if (exponent >= kMaxExponent) {
|
||||
return kInfinity;
|
||||
}
|
||||
if (exponent < kDenormalExponent) {
|
||||
return 0;
|
||||
}
|
||||
while (exponent > kDenormalExponent && (significand & kHiddenBit) == 0) {
|
||||
significand <<= 1;
|
||||
exponent--;
|
||||
}
|
||||
uint64_t biased_exponent;
|
||||
if (exponent == kDenormalExponent && (significand & kHiddenBit) == 0) {
|
||||
biased_exponent = 0;
|
||||
} else {
|
||||
biased_exponent = static_cast<uint64_t>(exponent + kExponentBias);
|
||||
}
|
||||
return (significand & kSignificandMask) |
|
||||
(biased_exponent << kPhysicalSignificandSize);
|
||||
}
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Double);
|
||||
};
|
||||
|
||||
class Single {
|
||||
public:
|
||||
static const uint32_t kSignMask = 0x80000000;
|
||||
static const uint32_t kExponentMask = 0x7F800000;
|
||||
static const uint32_t kSignificandMask = 0x007FFFFF;
|
||||
static const uint32_t kHiddenBit = 0x00800000;
|
||||
static const int kPhysicalSignificandSize = 23; // Excludes the hidden bit.
|
||||
static const int kSignificandSize = 24;
|
||||
|
||||
Single() : d32_(0) {}
|
||||
explicit Single(float f) : d32_(float_to_uint32(f)) {}
|
||||
explicit Single(uint32_t d32) : d32_(d32) {}
|
||||
|
||||
// The value encoded by this Single must be greater or equal to +0.0.
|
||||
// It must not be special (infinity, or NaN).
|
||||
DiyFp AsDiyFp() const {
|
||||
ASSERT(Sign() > 0);
|
||||
ASSERT(!IsSpecial());
|
||||
return DiyFp(Significand(), Exponent());
|
||||
}
|
||||
|
||||
// Returns the single's bit as uint64.
|
||||
uint32_t AsUint32() const {
|
||||
return d32_;
|
||||
}
|
||||
|
||||
int Exponent() const {
|
||||
if (IsDenormal()) return kDenormalExponent;
|
||||
|
||||
uint32_t d32 = AsUint32();
|
||||
int biased_e =
|
||||
static_cast<int>((d32 & kExponentMask) >> kPhysicalSignificandSize);
|
||||
return biased_e - kExponentBias;
|
||||
}
|
||||
|
||||
uint32_t Significand() const {
|
||||
uint32_t d32 = AsUint32();
|
||||
uint32_t significand = d32 & kSignificandMask;
|
||||
if (!IsDenormal()) {
|
||||
return significand + kHiddenBit;
|
||||
} else {
|
||||
return significand;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if the single is a denormal.
|
||||
bool IsDenormal() const {
|
||||
uint32_t d32 = AsUint32();
|
||||
return (d32 & kExponentMask) == 0;
|
||||
}
|
||||
|
||||
// We consider denormals not to be special.
|
||||
// Hence only Infinity and NaN are special.
|
||||
bool IsSpecial() const {
|
||||
uint32_t d32 = AsUint32();
|
||||
return (d32 & kExponentMask) == kExponentMask;
|
||||
}
|
||||
|
||||
bool IsNan() const {
|
||||
uint32_t d32 = AsUint32();
|
||||
return ((d32 & kExponentMask) == kExponentMask) &&
|
||||
((d32 & kSignificandMask) != 0);
|
||||
}
|
||||
|
||||
bool IsInfinite() const {
|
||||
uint32_t d32 = AsUint32();
|
||||
return ((d32 & kExponentMask) == kExponentMask) &&
|
||||
((d32 & kSignificandMask) == 0);
|
||||
}
|
||||
|
||||
int Sign() const {
|
||||
uint32_t d32 = AsUint32();
|
||||
return (d32 & kSignMask) == 0? 1: -1;
|
||||
}
|
||||
|
||||
// Computes the two boundaries of this.
|
||||
// The bigger boundary (m_plus) is normalized. The lower boundary has the same
|
||||
// exponent as m_plus.
|
||||
// Precondition: the value encoded by this Single must be greater than 0.
|
||||
void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const {
|
||||
ASSERT(value() > 0.0);
|
||||
DiyFp v = this->AsDiyFp();
|
||||
DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1));
|
||||
DiyFp m_minus;
|
||||
if (LowerBoundaryIsCloser()) {
|
||||
m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2);
|
||||
} else {
|
||||
m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1);
|
||||
}
|
||||
m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e()));
|
||||
m_minus.set_e(m_plus.e());
|
||||
*out_m_plus = m_plus;
|
||||
*out_m_minus = m_minus;
|
||||
}
|
||||
|
||||
// Precondition: the value encoded by this Single must be greater or equal
|
||||
// than +0.0.
|
||||
DiyFp UpperBoundary() const {
|
||||
ASSERT(Sign() > 0);
|
||||
return DiyFp(Significand() * 2 + 1, Exponent() - 1);
|
||||
}
|
||||
|
||||
bool LowerBoundaryIsCloser() const {
|
||||
// The boundary is closer if the significand is of the form f == 2^p-1 then
|
||||
// the lower boundary is closer.
|
||||
// Think of v = 1000e10 and v- = 9999e9.
|
||||
// Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but
|
||||
// at a distance of 1e8.
|
||||
// The only exception is for the smallest normal: the largest denormal is
|
||||
// at the same distance as its successor.
|
||||
// Note: denormals have the same exponent as the smallest normals.
|
||||
bool physical_significand_is_zero = ((AsUint32() & kSignificandMask) == 0);
|
||||
return physical_significand_is_zero && (Exponent() != kDenormalExponent);
|
||||
}
|
||||
|
||||
float value() const { return uint32_to_float(d32_); }
|
||||
|
||||
static float Infinity() {
|
||||
return Single(kInfinity).value();
|
||||
}
|
||||
|
||||
static float NaN() {
|
||||
return Single(kNaN).value();
|
||||
}
|
||||
|
||||
private:
|
||||
static const int kExponentBias = 0x7F + kPhysicalSignificandSize;
|
||||
static const int kDenormalExponent = -kExponentBias + 1;
|
||||
static const int kMaxExponent = 0xFF - kExponentBias;
|
||||
static const uint32_t kInfinity = 0x7F800000;
|
||||
static const uint32_t kNaN = 0x7FC00000;
|
||||
|
||||
const uint32_t d32_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Single);
|
||||
};
|
||||
|
||||
} // namespace double_conversion
|
||||
|
||||
#endif // DOUBLE_CONVERSION_DOUBLE_H_
|
||||
@ -1,45 +0,0 @@
|
||||
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef DOUBLE_CONVERSION_STRTOD_H_
|
||||
#define DOUBLE_CONVERSION_STRTOD_H_
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
namespace double_conversion {
|
||||
|
||||
// The buffer must only contain digits in the range [0-9]. It must not
|
||||
// contain a dot or a sign. It must not start with '0', and must not be empty.
|
||||
double Strtod(Vector<const char> buffer, int exponent);
|
||||
|
||||
// The buffer must only contain digits in the range [0-9]. It must not
|
||||
// contain a dot or a sign. It must not start with '0', and must not be empty.
|
||||
float Strtof(Vector<const char> buffer, int exponent);
|
||||
|
||||
} // namespace double_conversion
|
||||
|
||||
#endif // DOUBLE_CONVERSION_STRTOD_H_
|
||||
@ -1,324 +0,0 @@
|
||||
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef DOUBLE_CONVERSION_UTILS_H_
|
||||
#define DOUBLE_CONVERSION_UTILS_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <assert.h>
|
||||
#ifndef ASSERT
|
||||
#define ASSERT(condition) \
|
||||
assert(condition);
|
||||
#endif
|
||||
#ifndef UNIMPLEMENTED
|
||||
#define UNIMPLEMENTED() (abort())
|
||||
#endif
|
||||
#ifndef UNREACHABLE
|
||||
#define UNREACHABLE() (abort())
|
||||
#endif
|
||||
|
||||
// Double operations detection based on target architecture.
|
||||
// Linux uses a 80bit wide floating point stack on x86. This induces double
|
||||
// rounding, which in turn leads to wrong results.
|
||||
// An easy way to test if the floating-point operations are correct is to
|
||||
// evaluate: 89255.0/1e22. If the floating-point stack is 64 bits wide then
|
||||
// the result is equal to 89255e-22.
|
||||
// The best way to test this, is to create a division-function and to compare
|
||||
// the output of the division with the expected result. (Inlining must be
|
||||
// disabled.)
|
||||
// On Linux,x86 89255e-22 != Div_double(89255.0/1e22)
|
||||
#if defined(_M_X64) || defined(__x86_64__) || \
|
||||
defined(__ARMEL__) || defined(__avr32__) || \
|
||||
defined(__hppa__) || defined(__ia64__) || \
|
||||
defined(__mips__) || \
|
||||
defined(__powerpc__) || defined(__ppc__) || defined(__ppc64__) || \
|
||||
defined(__sparc__) || defined(__sparc) || defined(__s390__) || \
|
||||
defined(__SH4__) || defined(__alpha__) || \
|
||||
defined(_MIPS_ARCH_MIPS32R2) || \
|
||||
defined(__AARCH64EL__)
|
||||
#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1
|
||||
#elif defined(__mc68000__)
|
||||
#undef DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS
|
||||
#elif defined(_M_IX86) || defined(__i386__) || defined(__i386)
|
||||
#if defined(_WIN32)
|
||||
// Windows uses a 64bit wide floating point stack.
|
||||
#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1
|
||||
#else
|
||||
#undef DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS
|
||||
#endif // _WIN32
|
||||
#else
|
||||
#error Target architecture was not detected as supported by Double-Conversion.
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define DOUBLE_CONVERSION_UNUSED __attribute__((unused))
|
||||
#else
|
||||
#define DOUBLE_CONVERSION_UNUSED
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && !defined(__MINGW32__)
|
||||
|
||||
typedef signed char int8_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef short int16_t; // NOLINT
|
||||
typedef unsigned short uint16_t; // NOLINT
|
||||
typedef int int32_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
// intptr_t and friends are defined in crtdefs.h through stdio.h.
|
||||
|
||||
#else
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#endif
|
||||
|
||||
// The following macro works on both 32 and 64-bit platforms.
|
||||
// Usage: instead of writing 0x1234567890123456
|
||||
// write UINT64_2PART_C(0x12345678,90123456);
|
||||
#define UINT64_2PART_C(a, b) (((static_cast<uint64_t>(a) << 32) + 0x##b##u))
|
||||
|
||||
|
||||
// The expression ARRAY_SIZE(a) is a compile-time constant of type
|
||||
// size_t which represents the number of elements of the given
|
||||
// array. You should only use ARRAY_SIZE on statically allocated
|
||||
// arrays.
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(a) \
|
||||
((sizeof(a) / sizeof(*(a))) / \
|
||||
static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
|
||||
#endif
|
||||
|
||||
// A macro to disallow the evil copy constructor and operator= functions
|
||||
// This should be used in the private: declarations for a class
|
||||
#ifndef DISALLOW_COPY_AND_ASSIGN
|
||||
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
TypeName(const TypeName&); \
|
||||
void operator=(const TypeName&)
|
||||
#endif
|
||||
|
||||
// A macro to disallow all the implicit constructors, namely the
|
||||
// default constructor, copy constructor and operator= functions.
|
||||
//
|
||||
// This should be used in the private: declarations for a class
|
||||
// that wants to prevent anyone from instantiating it. This is
|
||||
// especially useful for classes containing only static methods.
|
||||
#ifndef DISALLOW_IMPLICIT_CONSTRUCTORS
|
||||
#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
|
||||
TypeName(); \
|
||||
DISALLOW_COPY_AND_ASSIGN(TypeName)
|
||||
#endif
|
||||
|
||||
namespace double_conversion {
|
||||
|
||||
static const int kCharSize = sizeof(char);
|
||||
|
||||
// Returns the maximum of the two parameters.
|
||||
template <typename T>
|
||||
static T Max(T a, T b) {
|
||||
return a < b ? b : a;
|
||||
}
|
||||
|
||||
|
||||
// Returns the minimum of the two parameters.
|
||||
template <typename T>
|
||||
static T Min(T a, T b) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
|
||||
inline int StrLength(const char* string) {
|
||||
size_t length = strlen(string);
|
||||
ASSERT(length == static_cast<size_t>(static_cast<int>(length)));
|
||||
return static_cast<int>(length);
|
||||
}
|
||||
|
||||
// This is a simplified version of V8's Vector class.
|
||||
template <typename T>
|
||||
class Vector {
|
||||
public:
|
||||
Vector() : start_(NULL), length_(0) {}
|
||||
Vector(T* data, int length) : start_(data), length_(length) {
|
||||
ASSERT(length == 0 || (length > 0 && data != NULL));
|
||||
}
|
||||
|
||||
// Returns a vector using the same backing storage as this one,
|
||||
// spanning from and including 'from', to but not including 'to'.
|
||||
Vector<T> SubVector(int from, int to) {
|
||||
ASSERT(to <= length_);
|
||||
ASSERT(from < to);
|
||||
ASSERT(0 <= from);
|
||||
return Vector<T>(start() + from, to - from);
|
||||
}
|
||||
|
||||
// Returns the length of the vector.
|
||||
int length() const { return length_; }
|
||||
|
||||
// Returns whether or not the vector is empty.
|
||||
bool is_empty() const { return length_ == 0; }
|
||||
|
||||
// Returns the pointer to the start of the data in the vector.
|
||||
T* start() const { return start_; }
|
||||
|
||||
// Access individual vector elements - checks bounds in debug mode.
|
||||
T& operator[](int index) const {
|
||||
ASSERT(0 <= index && index < length_);
|
||||
return start_[index];
|
||||
}
|
||||
|
||||
T& first() { return start_[0]; }
|
||||
|
||||
T& last() { return start_[length_ - 1]; }
|
||||
|
||||
private:
|
||||
T* start_;
|
||||
int length_;
|
||||
};
|
||||
|
||||
|
||||
// Helper class for building result strings in a character buffer. The
|
||||
// purpose of the class is to use safe operations that checks the
|
||||
// buffer bounds on all operations in debug mode.
|
||||
class StringBuilder {
|
||||
public:
|
||||
StringBuilder(char* buffer, int size)
|
||||
: buffer_(buffer, size), position_(0) { }
|
||||
|
||||
~StringBuilder() { if (!is_finalized()) Finalize(); }
|
||||
|
||||
int size() const { return buffer_.length(); }
|
||||
|
||||
// Get the current position in the builder.
|
||||
int position() const {
|
||||
ASSERT(!is_finalized());
|
||||
return position_;
|
||||
}
|
||||
|
||||
// Reset the position.
|
||||
void Reset() { position_ = 0; }
|
||||
|
||||
// Add a single character to the builder. It is not allowed to add
|
||||
// 0-characters; use the Finalize() method to terminate the string
|
||||
// instead.
|
||||
void AddCharacter(char c) {
|
||||
ASSERT(c != '\0');
|
||||
ASSERT(!is_finalized() && position_ < buffer_.length());
|
||||
buffer_[position_++] = c;
|
||||
}
|
||||
|
||||
// Add an entire string to the builder. Uses strlen() internally to
|
||||
// compute the length of the input string.
|
||||
void AddString(const char* s) {
|
||||
AddSubstring(s, StrLength(s));
|
||||
}
|
||||
|
||||
// Add the first 'n' characters of the given string 's' to the
|
||||
// builder. The input string must have enough characters.
|
||||
void AddSubstring(const char* s, int n) {
|
||||
ASSERT(!is_finalized() && position_ + n < buffer_.length());
|
||||
ASSERT(static_cast<size_t>(n) <= strlen(s));
|
||||
memmove(&buffer_[position_], s, n * kCharSize);
|
||||
position_ += n;
|
||||
}
|
||||
|
||||
|
||||
// Add character padding to the builder. If count is non-positive,
|
||||
// nothing is added to the builder.
|
||||
void AddPadding(char c, int count) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
AddCharacter(c);
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize the string by 0-terminating it and returning the buffer.
|
||||
char* Finalize() {
|
||||
ASSERT(!is_finalized() && position_ < buffer_.length());
|
||||
buffer_[position_] = '\0';
|
||||
// Make sure nobody managed to add a 0-character to the
|
||||
// buffer while building the string.
|
||||
ASSERT(strlen(buffer_.start()) == static_cast<size_t>(position_));
|
||||
position_ = -1;
|
||||
ASSERT(is_finalized());
|
||||
return buffer_.start();
|
||||
}
|
||||
|
||||
private:
|
||||
Vector<char> buffer_;
|
||||
int position_;
|
||||
|
||||
bool is_finalized() const { return position_ < 0; }
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder);
|
||||
};
|
||||
|
||||
// The type-based aliasing rule allows the compiler to assume that pointers of
|
||||
// different types (for some definition of different) never alias each other.
|
||||
// Thus the following code does not work:
|
||||
//
|
||||
// float f = foo();
|
||||
// int fbits = *(int*)(&f);
|
||||
//
|
||||
// The compiler 'knows' that the int pointer can't refer to f since the types
|
||||
// don't match, so the compiler may cache f in a register, leaving random data
|
||||
// in fbits. Using C++ style casts makes no difference, however a pointer to
|
||||
// char data is assumed to alias any other pointer. This is the 'memcpy
|
||||
// exception'.
|
||||
//
|
||||
// Bit_cast uses the memcpy exception to move the bits from a variable of one
|
||||
// type of a variable of another type. Of course the end result is likely to
|
||||
// be implementation dependent. Most compilers (gcc-4.2 and MSVC 2005)
|
||||
// will completely optimize BitCast away.
|
||||
//
|
||||
// There is an additional use for BitCast.
|
||||
// Recent gccs will warn when they see casts that may result in breakage due to
|
||||
// the type-based aliasing rule. If you have checked that there is no breakage
|
||||
// you can use BitCast to cast one pointer type to another. This confuses gcc
|
||||
// enough that it can no longer see that you have cast one pointer type to
|
||||
// another thus avoiding the warning.
|
||||
template <class Dest, class Source>
|
||||
inline Dest BitCast(const Source& source) {
|
||||
// Compile time assertion: sizeof(Dest) == sizeof(Source)
|
||||
// A compile error here means your Dest and Source have different sizes.
|
||||
DOUBLE_CONVERSION_UNUSED
|
||||
typedef char VerifySizesAreEqual[sizeof(Dest) == sizeof(Source) ? 1 : -1];
|
||||
|
||||
Dest dest;
|
||||
memmove(&dest, &source, sizeof(dest));
|
||||
return dest;
|
||||
}
|
||||
|
||||
template <class Dest, class Source>
|
||||
inline Dest BitCast(Source* source) {
|
||||
return BitCast<Dest>(reinterpret_cast<uintptr_t>(source));
|
||||
}
|
||||
|
||||
} // namespace double_conversion
|
||||
|
||||
#endif // DOUBLE_CONVERSION_UTILS_H_
|
||||
177
openvidu-react-native-androidx/ios/Pods/Folly/LICENSE
generated
@ -1,177 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
246
openvidu-react-native-androidx/ios/Pods/Folly/README.md
generated
@ -1,246 +0,0 @@
|
||||
Folly: Facebook Open-source Library
|
||||
-----------------------------------
|
||||
|
||||
[](https://travis-ci.org/facebook/folly)
|
||||
|
||||
### What is `folly`?
|
||||
|
||||
Folly (acronymed loosely after Facebook Open Source Library) is a
|
||||
library of C++14 components designed with practicality and efficiency
|
||||
in mind. **Folly contains a variety of core library components used extensively
|
||||
at Facebook**. In particular, it's often a dependency of Facebook's other
|
||||
open source C++ efforts and place where those projects can share code.
|
||||
|
||||
It complements (as opposed to competing against) offerings
|
||||
such as Boost and of course `std`. In fact, we embark on defining our
|
||||
own component only when something we need is either not available, or
|
||||
does not meet the needed performance profile. We endeavor to remove
|
||||
things from folly if or when `std` or Boost obsoletes them.
|
||||
|
||||
Performance concerns permeate much of Folly, sometimes leading to
|
||||
designs that are more idiosyncratic than they would otherwise be (see
|
||||
e.g. `PackedSyncPtr.h`, `SmallLocks.h`). Good performance at large
|
||||
scale is a unifying theme in all of Folly.
|
||||
|
||||
### Logical Design
|
||||
|
||||
Folly is a collection of relatively independent components, some as
|
||||
simple as a few symbols. There is no restriction on internal
|
||||
dependencies, meaning that a given folly module may use any other
|
||||
folly components.
|
||||
|
||||
All symbols are defined in the top-level namespace `folly`, except of
|
||||
course macros. Macro names are ALL_UPPERCASE and should be prefixed
|
||||
with `FOLLY_`. Namespace `folly` defines other internal namespaces
|
||||
such as `internal` or `detail`. User code should not depend on symbols
|
||||
in those namespaces.
|
||||
|
||||
Folly has an `experimental` directory as well. This designation connotes
|
||||
primarily that we feel the API may change heavily over time. This code,
|
||||
typically, is still in heavy use and is well tested.
|
||||
|
||||
### Physical Design
|
||||
|
||||
At the top level Folly uses the classic "stuttering" scheme
|
||||
`folly/folly` used by Boost and others. The first directory serves as
|
||||
an installation root of the library (with possible versioning a la
|
||||
`folly-1.0/`), and the second is to distinguish the library when
|
||||
including files, e.g. `#include <folly/FBString.h>`.
|
||||
|
||||
The directory structure is flat (mimicking the namespace structure),
|
||||
i.e. we don't have an elaborate directory hierarchy (it is possible
|
||||
this will change in future versions). The subdirectory `experimental`
|
||||
contains files that are used inside folly and possibly at Facebook but
|
||||
not considered stable enough for client use. Your code should not use
|
||||
files in `folly/experimental` lest it may break when you update Folly.
|
||||
|
||||
The `folly/folly/test` subdirectory includes the unittests for all
|
||||
components, usually named `ComponentXyzTest.cpp` for each
|
||||
`ComponentXyz.*`. The `folly/folly/docs` directory contains
|
||||
documentation.
|
||||
|
||||
### What's in it?
|
||||
|
||||
Because of folly's fairly flat structure, the best way to see what's in it
|
||||
is to look at the headers in [top level `folly/` directory](https://github.com/facebook/folly/tree/master/folly). You can also
|
||||
check the [`docs` folder](folly/docs) for documentation, starting with the
|
||||
[overview](folly/docs/Overview.md).
|
||||
|
||||
Folly is published on Github at https://github.com/facebook/folly
|
||||
|
||||
### Build Notes
|
||||
|
||||
#### Dependencies
|
||||
|
||||
folly requires gcc 4.9+ and a version of boost compiled with C++14 support.
|
||||
|
||||
googletest is required to build and run folly's tests. You can download
|
||||
it from https://github.com/google/googletest/archive/release-1.8.0.tar.gz
|
||||
The following commands can be used to download and install it:
|
||||
|
||||
```
|
||||
wget https://github.com/google/googletest/archive/release-1.8.0.tar.gz && \
|
||||
tar zxf release-1.8.0.tar.gz && \
|
||||
rm -f release-1.8.0.tar.gz && \
|
||||
cd googletest-release-1.8.0 && \
|
||||
cmake . && \
|
||||
make && \
|
||||
make install
|
||||
```
|
||||
|
||||
#### Finding dependencies in non-default locations
|
||||
|
||||
If you have boost, gtest, or other dependencies installed in a non-default
|
||||
location, you can use the `CMAKE_INCLUDE_PATH` and `CMAKE_LIBRARY_PATH`
|
||||
variables to make CMAKE look also look for header files and libraries in
|
||||
non-standard locations. For example, to also search the directories
|
||||
`/alt/include/path1` and `/alt/include/path2` for header files and the
|
||||
directories `/alt/lib/path1` and `/alt/lib/path2` for libraries, you can invoke
|
||||
`cmake` as follows:
|
||||
|
||||
```
|
||||
cmake \
|
||||
-DCMAKE_INCLUDE_PATH=/alt/include/path1:/alt/include/path2 \
|
||||
-DCMAKE_LIBRARY_PATH=/alt/lib/path1:/alt/lib/path2 ...
|
||||
```
|
||||
|
||||
#### Ubuntu 16.04 LTS
|
||||
|
||||
The following packages are required (feel free to cut and paste the apt-get
|
||||
command below):
|
||||
|
||||
```
|
||||
sudo apt-get install \
|
||||
g++ \
|
||||
cmake \
|
||||
libboost-all-dev \
|
||||
libevent-dev \
|
||||
libdouble-conversion-dev \
|
||||
libgoogle-glog-dev \
|
||||
libgflags-dev \
|
||||
libiberty-dev \
|
||||
liblz4-dev \
|
||||
liblzma-dev \
|
||||
libsnappy-dev \
|
||||
make \
|
||||
zlib1g-dev \
|
||||
binutils-dev \
|
||||
libjemalloc-dev \
|
||||
libssl-dev \
|
||||
pkg-config
|
||||
```
|
||||
|
||||
If advanced debugging functionality is required, use:
|
||||
|
||||
```
|
||||
sudo apt-get install \
|
||||
libunwind8-dev \
|
||||
libelf-dev \
|
||||
libdwarf-dev
|
||||
```
|
||||
|
||||
In the folly directory, run:
|
||||
```
|
||||
mkdir _build && cd _build
|
||||
cmake ..
|
||||
make -j $(nproc)
|
||||
make install
|
||||
```
|
||||
|
||||
#### OS X (Homebrew)
|
||||
|
||||
folly is available as a Formula and releases may be built via `brew install folly`.
|
||||
|
||||
You may also use `folly/build/bootstrap-osx-homebrew.sh` to build against `master`:
|
||||
|
||||
```
|
||||
cd folly
|
||||
./build/bootstrap-osx-homebrew.sh
|
||||
```
|
||||
|
||||
#### OS X (MacPorts)
|
||||
|
||||
Install the required packages from MacPorts:
|
||||
|
||||
```
|
||||
sudo port install \
|
||||
autoconf \
|
||||
automake \
|
||||
boost \
|
||||
gflags \
|
||||
git \
|
||||
google-glog \
|
||||
libevent \
|
||||
libtool \
|
||||
lz4 \
|
||||
lzma \
|
||||
scons \
|
||||
snappy \
|
||||
zlib
|
||||
```
|
||||
|
||||
Download and install double-conversion:
|
||||
|
||||
```
|
||||
git clone https://github.com/google/double-conversion.git
|
||||
cd double-conversion
|
||||
cmake -DBUILD_SHARED_LIBS=ON .
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
|
||||
Download and install folly with the parameters listed below:
|
||||
|
||||
```
|
||||
git clone https://github.com/facebook/folly.git
|
||||
cd folly/folly
|
||||
autoreconf -ivf
|
||||
./configure CPPFLAGS="-I/opt/local/include" LDFLAGS="-L/opt/local/lib"
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
|
||||
#### Windows (Vcpkg)
|
||||
|
||||
folly is available in [Vcpkg](https://github.com/Microsoft/vcpkg#vcpkg) and releases may be built via `vcpkg install folly:x64-windows`.
|
||||
|
||||
You may also use `vcpkg install folly:x64-windows --head` to build against `master`.
|
||||
|
||||
#### Other Linux distributions
|
||||
|
||||
- double-conversion (https://github.com/google/double-conversion)
|
||||
|
||||
Download and build double-conversion.
|
||||
You may need to tell cmake where to find it.
|
||||
|
||||
[double-conversion/] `ln -s src double-conversion`
|
||||
|
||||
[folly/] `mkdir build && cd build`
|
||||
[folly/build/] `cmake "-DCMAKE_INCLUDE_PATH=$DOUBLE_CONVERSION_HOME/include" "-DCMAKE_LIBRARY_PATH=$DOUBLE_CONVERSION_HOME/lib" ..`
|
||||
|
||||
[folly/build/] `make`
|
||||
|
||||
- additional platform specific dependencies:
|
||||
|
||||
Fedora >= 21 64-bit (last tested on Fedora 28 64-bit)
|
||||
- gcc
|
||||
- gcc-c++
|
||||
- cmake
|
||||
- automake
|
||||
- boost-devel
|
||||
- libtool
|
||||
- lz4-devel
|
||||
- lzma-devel
|
||||
- snappy-devel
|
||||
- zlib-devel
|
||||
- glog-devel
|
||||
- gflags-devel
|
||||
- scons
|
||||
- double-conversion-devel
|
||||
- openssl-devel
|
||||
- libevent-devel
|
||||
|
||||
Optional
|
||||
- libdwarf-dev
|
||||
- libelf-dev
|
||||
- libunwind8-dev
|
||||
@ -1,158 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
#include <folly/Portability.h>
|
||||
|
||||
namespace folly {
|
||||
|
||||
/**
|
||||
* An atomic bitset of fixed size (specified at compile time).
|
||||
*/
|
||||
template <size_t N>
|
||||
class AtomicBitSet : private boost::noncopyable {
|
||||
public:
|
||||
/**
|
||||
* Construct an AtomicBitSet; all bits are initially false.
|
||||
*/
|
||||
AtomicBitSet();
|
||||
|
||||
/**
|
||||
* Set bit idx to true, using the given memory order. Returns the
|
||||
* previous value of the bit.
|
||||
*
|
||||
* Note that the operation is a read-modify-write operation due to the use
|
||||
* of fetch_or.
|
||||
*/
|
||||
bool set(size_t idx, std::memory_order order = std::memory_order_seq_cst);
|
||||
|
||||
/**
|
||||
* Set bit idx to false, using the given memory order. Returns the
|
||||
* previous value of the bit.
|
||||
*
|
||||
* Note that the operation is a read-modify-write operation due to the use
|
||||
* of fetch_and.
|
||||
*/
|
||||
bool reset(size_t idx, std::memory_order order = std::memory_order_seq_cst);
|
||||
|
||||
/**
|
||||
* Set bit idx to the given value, using the given memory order. Returns
|
||||
* the previous value of the bit.
|
||||
*
|
||||
* Note that the operation is a read-modify-write operation due to the use
|
||||
* of fetch_and or fetch_or.
|
||||
*
|
||||
* Yes, this is an overload of set(), to keep as close to std::bitset's
|
||||
* interface as possible.
|
||||
*/
|
||||
bool set(
|
||||
size_t idx,
|
||||
bool value,
|
||||
std::memory_order order = std::memory_order_seq_cst);
|
||||
|
||||
/**
|
||||
* Read bit idx.
|
||||
*/
|
||||
bool test(size_t idx, std::memory_order order = std::memory_order_seq_cst)
|
||||
const;
|
||||
|
||||
/**
|
||||
* Same as test() with the default memory order.
|
||||
*/
|
||||
bool operator[](size_t idx) const;
|
||||
|
||||
/**
|
||||
* Return the size of the bitset.
|
||||
*/
|
||||
constexpr size_t size() const {
|
||||
return N;
|
||||
}
|
||||
|
||||
private:
|
||||
// Pick the largest lock-free type available
|
||||
#if (ATOMIC_LLONG_LOCK_FREE == 2)
|
||||
typedef unsigned long long BlockType;
|
||||
#elif (ATOMIC_LONG_LOCK_FREE == 2)
|
||||
typedef unsigned long BlockType;
|
||||
#else
|
||||
// Even if not lock free, what can we do?
|
||||
typedef unsigned int BlockType;
|
||||
#endif
|
||||
typedef std::atomic<BlockType> AtomicBlockType;
|
||||
|
||||
static constexpr size_t kBitsPerBlock =
|
||||
std::numeric_limits<BlockType>::digits;
|
||||
|
||||
static constexpr size_t blockIndex(size_t bit) {
|
||||
return bit / kBitsPerBlock;
|
||||
}
|
||||
|
||||
static constexpr size_t bitOffset(size_t bit) {
|
||||
return bit % kBitsPerBlock;
|
||||
}
|
||||
|
||||
// avoid casts
|
||||
static constexpr BlockType kOne = 1;
|
||||
|
||||
std::array<AtomicBlockType, N> data_;
|
||||
};
|
||||
|
||||
// value-initialize to zero
|
||||
template <size_t N>
|
||||
inline AtomicBitSet<N>::AtomicBitSet() : data_() {}
|
||||
|
||||
template <size_t N>
|
||||
inline bool AtomicBitSet<N>::set(size_t idx, std::memory_order order) {
|
||||
assert(idx < N * kBitsPerBlock);
|
||||
BlockType mask = kOne << bitOffset(idx);
|
||||
return data_[blockIndex(idx)].fetch_or(mask, order) & mask;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
inline bool AtomicBitSet<N>::reset(size_t idx, std::memory_order order) {
|
||||
assert(idx < N * kBitsPerBlock);
|
||||
BlockType mask = kOne << bitOffset(idx);
|
||||
return data_[blockIndex(idx)].fetch_and(~mask, order) & mask;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
inline bool
|
||||
AtomicBitSet<N>::set(size_t idx, bool value, std::memory_order order) {
|
||||
return value ? set(idx, order) : reset(idx, order);
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
inline bool AtomicBitSet<N>::test(size_t idx, std::memory_order order) const {
|
||||
assert(idx < N * kBitsPerBlock);
|
||||
BlockType mask = kOne << bitOffset(idx);
|
||||
return data_[blockIndex(idx)].load(order) & mask;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
inline bool AtomicBitSet<N>::operator[](size_t idx) const {
|
||||
return test(idx);
|
||||
}
|
||||
|
||||
} // namespace folly
|
||||
@ -1,448 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* AtomicHashArray is the building block for AtomicHashMap. It provides the
|
||||
* core lock-free functionality, but is limited by the fact that it cannot
|
||||
* grow past its initialization size and is a little more awkward (no public
|
||||
* constructor, for example). If you're confident that you won't run out of
|
||||
* space, don't mind the awkardness, and really need bare-metal performance,
|
||||
* feel free to use AHA directly.
|
||||
*
|
||||
* Check out AtomicHashMap.h for more thorough documentation on perf and
|
||||
* general pros and cons relative to other hash maps.
|
||||
*
|
||||
* @author Spencer Ahrens <sahrens@fb.com>
|
||||
* @author Jordan DeLong <delong.j@fb.com>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#define FOLLY_ATOMICHASHARRAY_H_
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include <boost/iterator/iterator_facade.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
#include <folly/ThreadCachedInt.h>
|
||||
#include <folly/Utility.h>
|
||||
#include <folly/hash/Hash.h>
|
||||
|
||||
namespace folly {
|
||||
|
||||
struct AtomicHashArrayLinearProbeFcn {
|
||||
inline size_t operator()(size_t idx, size_t /* numProbes */, size_t capacity)
|
||||
const {
|
||||
idx += 1; // linear probing
|
||||
|
||||
// Avoid modulus because it's slow
|
||||
return LIKELY(idx < capacity) ? idx : (idx - capacity);
|
||||
}
|
||||
};
|
||||
|
||||
struct AtomicHashArrayQuadraticProbeFcn {
|
||||
inline size_t operator()(size_t idx, size_t numProbes, size_t capacity)
|
||||
const {
|
||||
idx += numProbes; // quadratic probing
|
||||
|
||||
// Avoid modulus because it's slow
|
||||
return LIKELY(idx < capacity) ? idx : (idx - capacity);
|
||||
}
|
||||
};
|
||||
|
||||
// Enables specializing checkLegalKey without specializing its class.
|
||||
namespace detail {
|
||||
template <typename NotKeyT, typename KeyT>
|
||||
inline void checkLegalKeyIfKeyTImpl(
|
||||
NotKeyT /* ignored */,
|
||||
KeyT /* emptyKey */,
|
||||
KeyT /* lockedKey */,
|
||||
KeyT /* erasedKey */) {}
|
||||
|
||||
template <typename KeyT>
|
||||
inline void checkLegalKeyIfKeyTImpl(
|
||||
KeyT key_in,
|
||||
KeyT emptyKey,
|
||||
KeyT lockedKey,
|
||||
KeyT erasedKey) {
|
||||
DCHECK_NE(key_in, emptyKey);
|
||||
DCHECK_NE(key_in, lockedKey);
|
||||
DCHECK_NE(key_in, erasedKey);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template <
|
||||
class KeyT,
|
||||
class ValueT,
|
||||
class HashFcn = std::hash<KeyT>,
|
||||
class EqualFcn = std::equal_to<KeyT>,
|
||||
class Allocator = std::allocator<char>,
|
||||
class ProbeFcn = AtomicHashArrayLinearProbeFcn,
|
||||
class KeyConvertFcn = Identity>
|
||||
class AtomicHashMap;
|
||||
|
||||
template <
|
||||
class KeyT,
|
||||
class ValueT,
|
||||
class HashFcn = std::hash<KeyT>,
|
||||
class EqualFcn = std::equal_to<KeyT>,
|
||||
class Allocator = std::allocator<char>,
|
||||
class ProbeFcn = AtomicHashArrayLinearProbeFcn,
|
||||
class KeyConvertFcn = Identity>
|
||||
class AtomicHashArray : boost::noncopyable {
|
||||
static_assert(
|
||||
(std::is_convertible<KeyT, int32_t>::value ||
|
||||
std::is_convertible<KeyT, int64_t>::value ||
|
||||
std::is_convertible<KeyT, const void*>::value),
|
||||
"You are trying to use AtomicHashArray with disallowed key "
|
||||
"types. You must use atomically compare-and-swappable integer "
|
||||
"keys, or a different container class.");
|
||||
|
||||
public:
|
||||
typedef KeyT key_type;
|
||||
typedef ValueT mapped_type;
|
||||
typedef HashFcn hasher;
|
||||
typedef EqualFcn key_equal;
|
||||
typedef KeyConvertFcn key_convert;
|
||||
typedef std::pair<const KeyT, ValueT> value_type;
|
||||
typedef std::size_t size_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef value_type& reference;
|
||||
typedef const value_type& const_reference;
|
||||
typedef value_type* pointer;
|
||||
typedef const value_type* const_pointer;
|
||||
|
||||
const size_t capacity_;
|
||||
const size_t maxEntries_;
|
||||
const KeyT kEmptyKey_;
|
||||
const KeyT kLockedKey_;
|
||||
const KeyT kErasedKey_;
|
||||
|
||||
template <class ContT, class IterVal>
|
||||
struct aha_iterator;
|
||||
|
||||
typedef aha_iterator<const AtomicHashArray, const value_type> const_iterator;
|
||||
typedef aha_iterator<AtomicHashArray, value_type> iterator;
|
||||
|
||||
// You really shouldn't need this if you use the SmartPtr provided by create,
|
||||
// but if you really want to do something crazy like stick the released
|
||||
// pointer into a DescriminatedPtr or something, you'll need this to clean up
|
||||
// after yourself.
|
||||
static void destroy(AtomicHashArray*);
|
||||
|
||||
private:
|
||||
const size_t kAnchorMask_;
|
||||
|
||||
struct Deleter {
|
||||
void operator()(AtomicHashArray* ptr) {
|
||||
AtomicHashArray::destroy(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
typedef std::unique_ptr<AtomicHashArray, Deleter> SmartPtr;
|
||||
|
||||
/*
|
||||
* create --
|
||||
*
|
||||
* Creates AtomicHashArray objects. Use instead of constructor/destructor.
|
||||
*
|
||||
* We do things this way in order to avoid the perf penalty of a second
|
||||
* pointer indirection when composing these into AtomicHashMap, which needs
|
||||
* to store an array of pointers so that it can perform atomic operations on
|
||||
* them when growing.
|
||||
*
|
||||
* Instead of a mess of arguments, we take a max size and a Config struct to
|
||||
* simulate named ctor parameters. The Config struct has sensible defaults
|
||||
* for everything, but is overloaded - if you specify a positive capacity,
|
||||
* that will be used directly instead of computing it based on
|
||||
* maxLoadFactor.
|
||||
*
|
||||
* Create returns an AHA::SmartPtr which is a unique_ptr with a custom
|
||||
* deleter to make sure everything is cleaned up properly.
|
||||
*/
|
||||
struct Config {
|
||||
KeyT emptyKey;
|
||||
KeyT lockedKey;
|
||||
KeyT erasedKey;
|
||||
double maxLoadFactor;
|
||||
double growthFactor;
|
||||
uint32_t entryCountThreadCacheSize;
|
||||
size_t capacity; // if positive, overrides maxLoadFactor
|
||||
|
||||
// Cannot have constexpr ctor because some compilers rightly complain.
|
||||
Config()
|
||||
: emptyKey((KeyT)-1),
|
||||
lockedKey((KeyT)-2),
|
||||
erasedKey((KeyT)-3),
|
||||
maxLoadFactor(0.8),
|
||||
growthFactor(-1),
|
||||
entryCountThreadCacheSize(1000),
|
||||
capacity(0) {}
|
||||
};
|
||||
|
||||
// Cannot have pre-instantiated const Config instance because of SIOF.
|
||||
static SmartPtr create(size_t maxSize, const Config& c = Config());
|
||||
|
||||
/*
|
||||
* find --
|
||||
*
|
||||
*
|
||||
* Returns the iterator to the element if found, otherwise end().
|
||||
*
|
||||
* As an optional feature, the type of the key to look up (LookupKeyT) is
|
||||
* allowed to be different from the type of keys actually stored (KeyT).
|
||||
*
|
||||
* This enables use cases where materializing the key is costly and usually
|
||||
* redudant, e.g., canonicalizing/interning a set of strings and being able
|
||||
* to look up by StringPiece. To use this feature, LookupHashFcn must take
|
||||
* a LookupKeyT, and LookupEqualFcn must take KeyT and LookupKeyT as first
|
||||
* and second parameter, respectively.
|
||||
*
|
||||
* See folly/test/ArrayHashArrayTest.cpp for sample usage.
|
||||
*/
|
||||
template <
|
||||
typename LookupKeyT = key_type,
|
||||
typename LookupHashFcn = hasher,
|
||||
typename LookupEqualFcn = key_equal>
|
||||
iterator find(LookupKeyT k) {
|
||||
return iterator(
|
||||
this, findInternal<LookupKeyT, LookupHashFcn, LookupEqualFcn>(k).idx);
|
||||
}
|
||||
|
||||
template <
|
||||
typename LookupKeyT = key_type,
|
||||
typename LookupHashFcn = hasher,
|
||||
typename LookupEqualFcn = key_equal>
|
||||
const_iterator find(LookupKeyT k) const {
|
||||
return const_cast<AtomicHashArray*>(this)
|
||||
->find<LookupKeyT, LookupHashFcn, LookupEqualFcn>(k);
|
||||
}
|
||||
|
||||
/*
|
||||
* insert --
|
||||
*
|
||||
* Returns a pair with iterator to the element at r.first and bool success.
|
||||
* Retrieve the index with ret.first.getIndex().
|
||||
*
|
||||
* Fails on key collision (does not overwrite) or if map becomes
|
||||
* full, at which point no element is inserted, iterator is set to end(),
|
||||
* and success is set false. On collisions, success is set false, but the
|
||||
* iterator is set to the existing entry.
|
||||
*/
|
||||
std::pair<iterator, bool> insert(const value_type& r) {
|
||||
return emplace(r.first, r.second);
|
||||
}
|
||||
std::pair<iterator, bool> insert(value_type&& r) {
|
||||
return emplace(r.first, std::move(r.second));
|
||||
}
|
||||
|
||||
/*
|
||||
* emplace --
|
||||
*
|
||||
* Same contract as insert(), but performs in-place construction
|
||||
* of the value type using the specified arguments.
|
||||
*
|
||||
* Also, like find(), this method optionally allows 'key_in' to have a type
|
||||
* different from that stored in the table; see find(). If and only if no
|
||||
* equal key is already present, this method converts 'key_in' to a key of
|
||||
* type KeyT using the provided LookupKeyToKeyFcn.
|
||||
*/
|
||||
template <
|
||||
typename LookupKeyT = key_type,
|
||||
typename LookupHashFcn = hasher,
|
||||
typename LookupEqualFcn = key_equal,
|
||||
typename LookupKeyToKeyFcn = key_convert,
|
||||
typename... ArgTs>
|
||||
std::pair<iterator, bool> emplace(LookupKeyT key_in, ArgTs&&... vCtorArgs) {
|
||||
SimpleRetT ret = insertInternal<
|
||||
LookupKeyT,
|
||||
LookupHashFcn,
|
||||
LookupEqualFcn,
|
||||
LookupKeyToKeyFcn>(key_in, std::forward<ArgTs>(vCtorArgs)...);
|
||||
return std::make_pair(iterator(this, ret.idx), ret.success);
|
||||
}
|
||||
|
||||
// returns the number of elements erased - should never exceed 1
|
||||
size_t erase(KeyT k);
|
||||
|
||||
// clears all keys and values in the map and resets all counters. Not thread
|
||||
// safe.
|
||||
void clear();
|
||||
|
||||
// Exact number of elements in the map - note that readFull() acquires a
|
||||
// mutex. See folly/ThreadCachedInt.h for more details.
|
||||
size_t size() const {
|
||||
return numEntries_.readFull() - numErases_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
iterator begin() {
|
||||
iterator it(this, 0);
|
||||
it.advancePastEmpty();
|
||||
return it;
|
||||
}
|
||||
const_iterator begin() const {
|
||||
const_iterator it(this, 0);
|
||||
it.advancePastEmpty();
|
||||
return it;
|
||||
}
|
||||
|
||||
iterator end() {
|
||||
return iterator(this, capacity_);
|
||||
}
|
||||
const_iterator end() const {
|
||||
return const_iterator(this, capacity_);
|
||||
}
|
||||
|
||||
// See AtomicHashMap::findAt - access elements directly
|
||||
// WARNING: The following 2 functions will fail silently for hashtable
|
||||
// with capacity > 2^32
|
||||
iterator findAt(uint32_t idx) {
|
||||
DCHECK_LT(idx, capacity_);
|
||||
return iterator(this, idx);
|
||||
}
|
||||
const_iterator findAt(uint32_t idx) const {
|
||||
return const_cast<AtomicHashArray*>(this)->findAt(idx);
|
||||
}
|
||||
|
||||
iterator makeIter(size_t idx) {
|
||||
return iterator(this, idx);
|
||||
}
|
||||
const_iterator makeIter(size_t idx) const {
|
||||
return const_iterator(this, idx);
|
||||
}
|
||||
|
||||
// The max load factor allowed for this map
|
||||
double maxLoadFactor() const {
|
||||
return ((double)maxEntries_) / capacity_;
|
||||
}
|
||||
|
||||
void setEntryCountThreadCacheSize(uint32_t newSize) {
|
||||
numEntries_.setCacheSize(newSize);
|
||||
numPendingEntries_.setCacheSize(newSize);
|
||||
}
|
||||
|
||||
uint32_t getEntryCountThreadCacheSize() const {
|
||||
return numEntries_.getCacheSize();
|
||||
}
|
||||
|
||||
/* Private data and helper functions... */
|
||||
|
||||
private:
|
||||
friend class AtomicHashMap<
|
||||
KeyT,
|
||||
ValueT,
|
||||
HashFcn,
|
||||
EqualFcn,
|
||||
Allocator,
|
||||
ProbeFcn>;
|
||||
|
||||
struct SimpleRetT {
|
||||
size_t idx;
|
||||
bool success;
|
||||
SimpleRetT(size_t i, bool s) : idx(i), success(s) {}
|
||||
SimpleRetT() = default;
|
||||
};
|
||||
|
||||
template <
|
||||
typename LookupKeyT = key_type,
|
||||
typename LookupHashFcn = hasher,
|
||||
typename LookupEqualFcn = key_equal,
|
||||
typename LookupKeyToKeyFcn = Identity,
|
||||
typename... ArgTs>
|
||||
SimpleRetT insertInternal(LookupKeyT key, ArgTs&&... vCtorArgs);
|
||||
|
||||
template <
|
||||
typename LookupKeyT = key_type,
|
||||
typename LookupHashFcn = hasher,
|
||||
typename LookupEqualFcn = key_equal>
|
||||
SimpleRetT findInternal(const LookupKeyT key);
|
||||
|
||||
template <typename MaybeKeyT>
|
||||
void checkLegalKeyIfKey(MaybeKeyT key) {
|
||||
detail::checkLegalKeyIfKeyTImpl(key, kEmptyKey_, kLockedKey_, kErasedKey_);
|
||||
}
|
||||
|
||||
static std::atomic<KeyT>* cellKeyPtr(const value_type& r) {
|
||||
// We need some illegal casting here in order to actually store
|
||||
// our value_type as a std::pair<const,>. But a little bit of
|
||||
// undefined behavior never hurt anyone ...
|
||||
static_assert(
|
||||
sizeof(std::atomic<KeyT>) == sizeof(KeyT),
|
||||
"std::atomic is implemented in an unexpected way for AHM");
|
||||
return const_cast<std::atomic<KeyT>*>(
|
||||
reinterpret_cast<std::atomic<KeyT> const*>(&r.first));
|
||||
}
|
||||
|
||||
static KeyT relaxedLoadKey(const value_type& r) {
|
||||
return cellKeyPtr(r)->load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
static KeyT acquireLoadKey(const value_type& r) {
|
||||
return cellKeyPtr(r)->load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
// Fun with thread local storage - atomic increment is expensive
|
||||
// (relatively), so we accumulate in the thread cache and periodically
|
||||
// flush to the actual variable, and walk through the unflushed counts when
|
||||
// reading the value, so be careful of calling size() too frequently. This
|
||||
// increases insertion throughput several times over while keeping the count
|
||||
// accurate.
|
||||
ThreadCachedInt<uint64_t> numEntries_; // Successful key inserts
|
||||
ThreadCachedInt<uint64_t> numPendingEntries_; // Used by insertInternal
|
||||
std::atomic<int64_t> isFull_; // Used by insertInternal
|
||||
std::atomic<int64_t> numErases_; // Successful key erases
|
||||
|
||||
value_type cells_[0]; // This must be the last field of this class
|
||||
|
||||
// Force constructor/destructor private since create/destroy should be
|
||||
// used externally instead
|
||||
AtomicHashArray(
|
||||
size_t capacity,
|
||||
KeyT emptyKey,
|
||||
KeyT lockedKey,
|
||||
KeyT erasedKey,
|
||||
double maxLoadFactor,
|
||||
uint32_t cacheSize);
|
||||
|
||||
~AtomicHashArray() = default;
|
||||
|
||||
inline void unlockCell(value_type* const cell, KeyT newKey) {
|
||||
cellKeyPtr(*cell)->store(newKey, std::memory_order_release);
|
||||
}
|
||||
|
||||
inline bool tryLockCell(value_type* const cell) {
|
||||
KeyT expect = kEmptyKey_;
|
||||
return cellKeyPtr(*cell)->compare_exchange_strong(
|
||||
expect, kLockedKey_, std::memory_order_acq_rel);
|
||||
}
|
||||
|
||||
template <class LookupKeyT = key_type, class LookupHashFcn = hasher>
|
||||
inline size_t keyToAnchorIdx(const LookupKeyT k) const {
|
||||
const size_t hashVal = LookupHashFcn()(k);
|
||||
const size_t probe = hashVal & kAnchorMask_;
|
||||
return LIKELY(probe < capacity_) ? probe : hashVal % capacity_;
|
||||
}
|
||||
|
||||
}; // AtomicHashArray
|
||||
|
||||
} // namespace folly
|
||||
|
||||
#include <folly/AtomicHashArray-inl.h>
|
||||
@ -1,178 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
|
||||
namespace folly {
|
||||
|
||||
/**
|
||||
* A very simple atomic single-linked list primitive.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* class MyClass {
|
||||
* AtomicIntrusiveLinkedListHook<MyClass> hook_;
|
||||
* }
|
||||
*
|
||||
* AtomicIntrusiveLinkedList<MyClass, &MyClass::hook_> list;
|
||||
* list.insert(&a);
|
||||
* list.sweep([] (MyClass* c) { doSomething(c); }
|
||||
*/
|
||||
template <class T>
|
||||
struct AtomicIntrusiveLinkedListHook {
|
||||
T* next{nullptr};
|
||||
};
|
||||
|
||||
template <class T, AtomicIntrusiveLinkedListHook<T> T::*HookMember>
|
||||
class AtomicIntrusiveLinkedList {
|
||||
public:
|
||||
AtomicIntrusiveLinkedList() {}
|
||||
AtomicIntrusiveLinkedList(const AtomicIntrusiveLinkedList&) = delete;
|
||||
AtomicIntrusiveLinkedList& operator=(const AtomicIntrusiveLinkedList&) =
|
||||
delete;
|
||||
AtomicIntrusiveLinkedList(AtomicIntrusiveLinkedList&& other) noexcept {
|
||||
auto tmp = other.head_.load();
|
||||
other.head_ = head_.load();
|
||||
head_ = tmp;
|
||||
}
|
||||
AtomicIntrusiveLinkedList& operator=(
|
||||
AtomicIntrusiveLinkedList&& other) noexcept {
|
||||
auto tmp = other.head_.load();
|
||||
other.head_ = head_.load();
|
||||
head_ = tmp;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: list must be empty on destruction.
|
||||
*/
|
||||
~AtomicIntrusiveLinkedList() {
|
||||
assert(empty());
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return head_.load() == nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically insert t at the head of the list.
|
||||
* @return True if the inserted element is the only one in the list
|
||||
* after the call.
|
||||
*/
|
||||
bool insertHead(T* t) {
|
||||
assert(next(t) == nullptr);
|
||||
|
||||
auto oldHead = head_.load(std::memory_order_relaxed);
|
||||
do {
|
||||
next(t) = oldHead;
|
||||
/* oldHead is updated by the call below.
|
||||
|
||||
NOTE: we don't use next(t) instead of oldHead directly due to
|
||||
compiler bugs (GCC prior to 4.8.3 (bug 60272), clang (bug 18899),
|
||||
MSVC (bug 819819); source:
|
||||
http://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange */
|
||||
} while (!head_.compare_exchange_weak(
|
||||
oldHead, t, std::memory_order_release, std::memory_order_relaxed));
|
||||
|
||||
return oldHead == nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the head with nullptr,
|
||||
* and calls func() on the removed elements in the order from tail to head.
|
||||
* Returns false if the list was empty.
|
||||
*/
|
||||
template <typename F>
|
||||
bool sweepOnce(F&& func) {
|
||||
if (auto head = head_.exchange(nullptr)) {
|
||||
auto rhead = reverse(head);
|
||||
unlinkAll(rhead, std::forward<F>(func));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Repeatedly replaces the head with nullptr,
|
||||
* and calls func() on the removed elements in the order from tail to head.
|
||||
* Stops when the list is empty.
|
||||
*/
|
||||
template <typename F>
|
||||
void sweep(F&& func) {
|
||||
while (sweepOnce(func)) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to sweep() but calls func() on elements in LIFO order.
|
||||
*
|
||||
* func() is called for all elements in the list at the moment
|
||||
* reverseSweep() is called. Unlike sweep() it does not loop to ensure the
|
||||
* list is empty at some point after the last invocation. This way callers
|
||||
* can reason about the ordering: elements inserted since the last call to
|
||||
* reverseSweep() will be provided in LIFO order.
|
||||
*
|
||||
* Example: if elements are inserted in the order 1-2-3, the callback is
|
||||
* invoked 3-2-1. If the callback moves elements onto a stack, popping off
|
||||
* the stack will produce the original insertion order 1-2-3.
|
||||
*/
|
||||
template <typename F>
|
||||
void reverseSweep(F&& func) {
|
||||
// We don't loop like sweep() does because the overall order of callbacks
|
||||
// would be strand-wise LIFO which is meaningless to callers.
|
||||
auto head = head_.exchange(nullptr);
|
||||
unlinkAll(head, std::forward<F>(func));
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<T*> head_{nullptr};
|
||||
|
||||
static T*& next(T* t) {
|
||||
return (t->*HookMember).next;
|
||||
}
|
||||
|
||||
/* Reverses a linked list, returning the pointer to the new head
|
||||
(old tail) */
|
||||
static T* reverse(T* head) {
|
||||
T* rhead = nullptr;
|
||||
while (head != nullptr) {
|
||||
auto t = head;
|
||||
head = next(t);
|
||||
next(t) = rhead;
|
||||
rhead = t;
|
||||
}
|
||||
return rhead;
|
||||
}
|
||||
|
||||
/* Unlinks all elements in the linked list fragment pointed to by `head',
|
||||
* calling func() on every element */
|
||||
template <typename F>
|
||||
void unlinkAll(T* head, F&& func) {
|
||||
while (head != nullptr) {
|
||||
auto t = head;
|
||||
head = next(t);
|
||||
next(t) = nullptr;
|
||||
func(t);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace folly
|
||||
@ -1,108 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <folly/AtomicIntrusiveLinkedList.h>
|
||||
#include <folly/Memory.h>
|
||||
|
||||
namespace folly {
|
||||
|
||||
/**
|
||||
* A very simple atomic single-linked list primitive.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* AtomicLinkedList<MyClass> list;
|
||||
* list.insert(a);
|
||||
* list.sweep([] (MyClass& c) { doSomething(c); }
|
||||
*/
|
||||
|
||||
template <class T>
|
||||
class AtomicLinkedList {
|
||||
public:
|
||||
AtomicLinkedList() {}
|
||||
AtomicLinkedList(const AtomicLinkedList&) = delete;
|
||||
AtomicLinkedList& operator=(const AtomicLinkedList&) = delete;
|
||||
AtomicLinkedList(AtomicLinkedList&& other) noexcept = default;
|
||||
AtomicLinkedList& operator=(AtomicLinkedList&& other) = default;
|
||||
|
||||
~AtomicLinkedList() {
|
||||
sweep([](T&&) {});
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return list_.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically insert t at the head of the list.
|
||||
* @return True if the inserted element is the only one in the list
|
||||
* after the call.
|
||||
*/
|
||||
bool insertHead(T t) {
|
||||
auto wrapper = std::make_unique<Wrapper>(std::move(t));
|
||||
|
||||
return list_.insertHead(wrapper.release());
|
||||
}
|
||||
|
||||
/**
|
||||
* Repeatedly pops element from head,
|
||||
* and calls func() on the removed elements in the order from tail to head.
|
||||
* Stops when the list is empty.
|
||||
*/
|
||||
template <typename F>
|
||||
void sweep(F&& func) {
|
||||
list_.sweep([&](Wrapper* wrapperPtr) mutable {
|
||||
std::unique_ptr<Wrapper> wrapper(wrapperPtr);
|
||||
|
||||
func(std::move(wrapper->data));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to sweep() but calls func() on elements in LIFO order.
|
||||
*
|
||||
* func() is called for all elements in the list at the moment
|
||||
* reverseSweep() is called. Unlike sweep() it does not loop to ensure the
|
||||
* list is empty at some point after the last invocation. This way callers
|
||||
* can reason about the ordering: elements inserted since the last call to
|
||||
* reverseSweep() will be provided in LIFO order.
|
||||
*
|
||||
* Example: if elements are inserted in the order 1-2-3, the callback is
|
||||
* invoked 3-2-1. If the callback moves elements onto a stack, popping off
|
||||
* the stack will produce the original insertion order 1-2-3.
|
||||
*/
|
||||
template <typename F>
|
||||
void reverseSweep(F&& func) {
|
||||
list_.reverseSweep([&](Wrapper* wrapperPtr) mutable {
|
||||
std::unique_ptr<Wrapper> wrapper(wrapperPtr);
|
||||
|
||||
func(std::move(wrapper->data));
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
struct Wrapper {
|
||||
explicit Wrapper(T&& t) : data(std::move(t)) {}
|
||||
|
||||
AtomicIntrusiveLinkedListHook<Wrapper> hook;
|
||||
T data;
|
||||
};
|
||||
AtomicIntrusiveLinkedList<Wrapper, &Wrapper::hook> list_;
|
||||
};
|
||||
|
||||
} // namespace folly
|
||||
@ -1,17 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <folly/lang/Bits.h> // @shim
|
||||
@ -1,194 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* These definitions are in a separate file so that they
|
||||
* may be included from C- as well as C++-based projects. */
|
||||
|
||||
#include <folly/portability/Config.h>
|
||||
|
||||
/**
|
||||
* Portable version check.
|
||||
*/
|
||||
#ifndef __GNUC_PREREQ
|
||||
#if defined __GNUC__ && defined __GNUC_MINOR__
|
||||
/* nolint */
|
||||
#define __GNUC_PREREQ(maj, min) \
|
||||
((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
|
||||
#else
|
||||
/* nolint */
|
||||
#define __GNUC_PREREQ(maj, min) 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// portable version check for clang
|
||||
#ifndef __CLANG_PREREQ
|
||||
#if defined __clang__ && defined __clang_major__ && defined __clang_minor__
|
||||
/* nolint */
|
||||
#define __CLANG_PREREQ(maj, min) \
|
||||
((__clang_major__ << 16) + __clang_minor__ >= ((maj) << 16) + (min))
|
||||
#else
|
||||
/* nolint */
|
||||
#define __CLANG_PREREQ(maj, min) 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__has_builtin)
|
||||
#define FOLLY_HAS_BUILTIN(...) __has_builtin(__VA_ARGS__)
|
||||
#else
|
||||
#define FOLLY_HAS_BUILTIN(...) 0
|
||||
#endif
|
||||
|
||||
#if defined(__has_feature)
|
||||
#define FOLLY_HAS_FEATURE(...) __has_feature(__VA_ARGS__)
|
||||
#else
|
||||
#define FOLLY_HAS_FEATURE(...) 0
|
||||
#endif
|
||||
|
||||
/* FOLLY_SANITIZE_ADDRESS is defined to 1 if the current compilation unit
|
||||
* is being compiled with ASAN enabled.
|
||||
*
|
||||
* Beware when using this macro in a header file: this macro may change values
|
||||
* across compilation units if some libraries are built with ASAN enabled
|
||||
* and some built with ASAN disabled. For instance, this may occur, if folly
|
||||
* itself was compiled without ASAN but a downstream project that uses folly is
|
||||
* compiling with ASAN enabled.
|
||||
*
|
||||
* Use FOLLY_ASAN_ENABLED (defined in folly-config.h) to check if folly itself
|
||||
* was compiled with ASAN enabled.
|
||||
*/
|
||||
#if FOLLY_HAS_FEATURE(address_sanitizer) || __SANITIZE_ADDRESS__
|
||||
#define FOLLY_SANITIZE_ADDRESS 1
|
||||
#endif
|
||||
|
||||
/* Define attribute wrapper for function attribute used to disable
|
||||
* address sanitizer instrumentation. Unfortunately, this attribute
|
||||
* has issues when inlining is used, so disable that as well. */
|
||||
#ifdef FOLLY_SANITIZE_ADDRESS
|
||||
#if defined(__clang__)
|
||||
#if __has_attribute(__no_sanitize__)
|
||||
#define FOLLY_DISABLE_ADDRESS_SANITIZER \
|
||||
__attribute__((__no_sanitize__("address"), __noinline__))
|
||||
#elif __has_attribute(__no_address_safety_analysis__)
|
||||
#define FOLLY_DISABLE_ADDRESS_SANITIZER \
|
||||
__attribute__((__no_address_safety_analysis__, __noinline__))
|
||||
#elif __has_attribute(__no_sanitize_address__)
|
||||
#define FOLLY_DISABLE_ADDRESS_SANITIZER \
|
||||
__attribute__((__no_sanitize_address__, __noinline__))
|
||||
#endif
|
||||
#elif defined(__GNUC__)
|
||||
#define FOLLY_DISABLE_ADDRESS_SANITIZER \
|
||||
__attribute__((__no_address_safety_analysis__, __noinline__))
|
||||
#endif
|
||||
#endif
|
||||
#ifndef FOLLY_DISABLE_ADDRESS_SANITIZER
|
||||
#define FOLLY_DISABLE_ADDRESS_SANITIZER
|
||||
#endif
|
||||
|
||||
/* Define a convenience macro to test when thread sanitizer is being used
|
||||
* across the different compilers (e.g. clang, gcc) */
|
||||
#if FOLLY_HAS_FEATURE(thread_sanitizer) || __SANITIZE_THREAD__
|
||||
#define FOLLY_SANITIZE_THREAD 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Define a convenience macro to test when ASAN, UBSAN or TSAN sanitizer are
|
||||
* being used
|
||||
*/
|
||||
#if defined(FOLLY_SANITIZE_ADDRESS) || defined(FOLLY_SANITIZE_THREAD)
|
||||
#define FOLLY_SANITIZE 1
|
||||
#endif
|
||||
|
||||
#if FOLLY_SANITIZE
|
||||
#define FOLLY_DISABLE_UNDEFINED_BEHAVIOR_SANITIZER(...) \
|
||||
__attribute__((no_sanitize(__VA_ARGS__)))
|
||||
#else
|
||||
#define FOLLY_DISABLE_UNDEFINED_BEHAVIOR_SANITIZER(...)
|
||||
#endif // FOLLY_SANITIZE
|
||||
|
||||
/**
|
||||
* Macro for marking functions as having public visibility.
|
||||
*/
|
||||
#if defined(__GNUC__)
|
||||
#if __GNUC_PREREQ(4, 9)
|
||||
#define FOLLY_EXPORT [[gnu::visibility("default")]]
|
||||
#else
|
||||
#define FOLLY_EXPORT __attribute__((__visibility__("default")))
|
||||
#endif
|
||||
#else
|
||||
#define FOLLY_EXPORT
|
||||
#endif
|
||||
|
||||
// noinline
|
||||
#ifdef _MSC_VER
|
||||
#define FOLLY_NOINLINE __declspec(noinline)
|
||||
#elif defined(__clang__) || defined(__GNUC__)
|
||||
#define FOLLY_NOINLINE __attribute__((__noinline__))
|
||||
#else
|
||||
#define FOLLY_NOINLINE
|
||||
#endif
|
||||
|
||||
// always inline
|
||||
#ifdef _MSC_VER
|
||||
#define FOLLY_ALWAYS_INLINE __forceinline
|
||||
#elif defined(__clang__) || defined(__GNUC__)
|
||||
#define FOLLY_ALWAYS_INLINE inline __attribute__((__always_inline__))
|
||||
#else
|
||||
#define FOLLY_ALWAYS_INLINE inline
|
||||
#endif
|
||||
|
||||
// attribute hidden
|
||||
#if _MSC_VER
|
||||
#define FOLLY_ATTR_VISIBILITY_HIDDEN
|
||||
#elif defined(__clang__) || defined(__GNUC__)
|
||||
#define FOLLY_ATTR_VISIBILITY_HIDDEN __attribute__((__visibility__("hidden")))
|
||||
#else
|
||||
#define FOLLY_ATTR_VISIBILITY_HIDDEN
|
||||
#endif
|
||||
|
||||
// An attribute for marking symbols as weak, if supported
|
||||
#if FOLLY_HAVE_WEAK_SYMBOLS
|
||||
#define FOLLY_ATTR_WEAK __attribute__((__weak__))
|
||||
#else
|
||||
#define FOLLY_ATTR_WEAK
|
||||
#endif
|
||||
|
||||
// Microsoft ABI version (can be overridden manually if necessary)
|
||||
#ifndef FOLLY_MICROSOFT_ABI_VER
|
||||
#ifdef _MSC_VER
|
||||
#define FOLLY_MICROSOFT_ABI_VER _MSC_VER
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// These functions are defined by the TSAN runtime library and enable
|
||||
// annotating mutexes for TSAN.
|
||||
extern "C" FOLLY_ATTR_WEAK void
|
||||
AnnotateRWLockCreate(const char* f, int l, const volatile void* addr);
|
||||
extern "C" FOLLY_ATTR_WEAK void
|
||||
AnnotateRWLockCreateStatic(const char* f, int l, const volatile void* addr);
|
||||
extern "C" FOLLY_ATTR_WEAK void
|
||||
AnnotateRWLockDestroy(const char* f, int l, const volatile void* addr);
|
||||
extern "C" FOLLY_ATTR_WEAK void
|
||||
AnnotateRWLockAcquired(const char* f, int l, const volatile void* addr, long w);
|
||||
extern "C" FOLLY_ATTR_WEAK void
|
||||
AnnotateRWLockReleased(const char* f, int l, const volatile void* addr, long w);
|
||||
extern "C" FOLLY_ATTR_WEAK void AnnotateBenignRaceSized(
|
||||
const char* f,
|
||||
int l,
|
||||
const volatile void* addr,
|
||||
long size,
|
||||
const char* desc);
|
||||
@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
#include <folly/lang/Align.h>
|
||||
|
||||
namespace folly {
|
||||
|
||||
/**
|
||||
* Holds a type T, in addition to enough padding to ensure that it isn't subject
|
||||
* to false sharing within the range used by folly.
|
||||
*
|
||||
* If `sizeof(T) <= alignof(T)` then the inner `T` will be entirely within one
|
||||
* false sharing range (AKA cache line).
|
||||
*/
|
||||
template <typename T>
|
||||
class CachelinePadded {
|
||||
static_assert(
|
||||
alignof(T) <= max_align_v,
|
||||
"CachelinePadded does not support over-aligned types.");
|
||||
|
||||
public:
|
||||
template <typename... Args>
|
||||
explicit CachelinePadded(Args&&... args)
|
||||
: inner_(std::forward<Args>(args)...) {}
|
||||
|
||||
T* get() {
|
||||
return &inner_;
|
||||
}
|
||||
|
||||
const T* get() const {
|
||||
return &inner_;
|
||||
}
|
||||
|
||||
T* operator->() {
|
||||
return get();
|
||||
}
|
||||
|
||||
const T* operator->() const {
|
||||
return get();
|
||||
}
|
||||
|
||||
T& operator*() {
|
||||
return *get();
|
||||
}
|
||||
|
||||
const T& operator*() const {
|
||||
return *get();
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr size_t paddingSize() noexcept {
|
||||
return hardware_destructive_interference_size -
|
||||
(alignof(T) % hardware_destructive_interference_size);
|
||||
}
|
||||
char paddingPre_[paddingSize()];
|
||||
T inner_;
|
||||
char paddingPost_[paddingSize()];
|
||||
};
|
||||
} // namespace folly
|
||||
@ -1,190 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
#include <folly/Portability.h>
|
||||
#include <folly/lang/Exception.h>
|
||||
#include <folly/portability/Time.h>
|
||||
|
||||
/***
|
||||
* include or backport:
|
||||
* * std::chrono::ceil
|
||||
* * std::chrono::floor
|
||||
* * std::chrono::round
|
||||
*/
|
||||
|
||||
#if __cpp_lib_chrono >= 201510 || _MSC_VER
|
||||
|
||||
namespace folly {
|
||||
namespace chrono {
|
||||
|
||||
/* using override */ using std::chrono::ceil;
|
||||
/* using override */ using std::chrono::floor;
|
||||
/* using override */ using std::chrono::round;
|
||||
} // namespace chrono
|
||||
} // namespace folly
|
||||
|
||||
#else
|
||||
|
||||
namespace folly {
|
||||
namespace chrono {
|
||||
|
||||
namespace detail {
|
||||
|
||||
// from: http://en.cppreference.com/w/cpp/chrono/duration/ceil, CC-BY-SA
|
||||
template <typename T>
|
||||
struct is_duration : std::false_type {};
|
||||
template <typename Rep, typename Period>
|
||||
struct is_duration<std::chrono::duration<Rep, Period>> : std::true_type {};
|
||||
|
||||
template <typename To, typename Duration>
|
||||
constexpr To ceil_impl(Duration const& d, To const& t) {
|
||||
return t < d ? t + To{1} : t;
|
||||
}
|
||||
|
||||
template <typename To, typename Duration>
|
||||
constexpr To floor_impl(Duration const& d, To const& t) {
|
||||
return t > d ? t - To{1} : t;
|
||||
}
|
||||
|
||||
template <typename To, typename Diff>
|
||||
constexpr To round_impl(To const& t0, To const& t1, Diff diff0, Diff diff1) {
|
||||
return diff0 < diff1 ? t0 : diff1 < diff0 ? t1 : t0.count() & 1 ? t1 : t0;
|
||||
}
|
||||
|
||||
template <typename To, typename Duration>
|
||||
constexpr To round_impl(Duration const& d, To const& t0, To const& t1) {
|
||||
return round_impl(t0, t1, d - t0, t1 - d);
|
||||
}
|
||||
|
||||
template <typename To, typename Duration>
|
||||
constexpr To round_impl(Duration const& d, To const& t0) {
|
||||
return round_impl(d, t0, t0 + To{1});
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
// mimic: std::chrono::ceil, C++17
|
||||
// from: http://en.cppreference.com/w/cpp/chrono/duration/ceil, CC-BY-SA
|
||||
template <
|
||||
typename To,
|
||||
typename Rep,
|
||||
typename Period,
|
||||
typename = typename std::enable_if<detail::is_duration<To>::value>::type>
|
||||
constexpr To ceil(std::chrono::duration<Rep, Period> const& d) {
|
||||
return detail::ceil_impl(d, std::chrono::duration_cast<To>(d));
|
||||
}
|
||||
|
||||
// mimic: std::chrono::ceil, C++17
|
||||
// from: http://en.cppreference.com/w/cpp/chrono/time_point/ceil, CC-BY-SA
|
||||
template <
|
||||
typename To,
|
||||
typename Clock,
|
||||
typename Duration,
|
||||
typename = typename std::enable_if<detail::is_duration<To>::value>::type>
|
||||
constexpr std::chrono::time_point<Clock, To> ceil(
|
||||
std::chrono::time_point<Clock, Duration> const& tp) {
|
||||
return std::chrono::time_point<Clock, To>{ceil<To>(tp.time_since_epoch())};
|
||||
}
|
||||
|
||||
// mimic: std::chrono::floor, C++17
|
||||
// from: http://en.cppreference.com/w/cpp/chrono/duration/floor, CC-BY-SA
|
||||
template <
|
||||
typename To,
|
||||
typename Rep,
|
||||
typename Period,
|
||||
typename = typename std::enable_if<detail::is_duration<To>::value>::type>
|
||||
constexpr To floor(std::chrono::duration<Rep, Period> const& d) {
|
||||
return detail::floor_impl(d, std::chrono::duration_cast<To>(d));
|
||||
}
|
||||
|
||||
// mimic: std::chrono::floor, C++17
|
||||
// from: http://en.cppreference.com/w/cpp/chrono/time_point/floor, CC-BY-SA
|
||||
template <
|
||||
typename To,
|
||||
typename Clock,
|
||||
typename Duration,
|
||||
typename = typename std::enable_if<detail::is_duration<To>::value>::type>
|
||||
constexpr std::chrono::time_point<Clock, To> floor(
|
||||
std::chrono::time_point<Clock, Duration> const& tp) {
|
||||
return std::chrono::time_point<Clock, To>{floor<To>(tp.time_since_epoch())};
|
||||
}
|
||||
|
||||
// mimic: std::chrono::round, C++17
|
||||
// from: http://en.cppreference.com/w/cpp/chrono/duration/round, CC-BY-SA
|
||||
template <
|
||||
typename To,
|
||||
typename Rep,
|
||||
typename Period,
|
||||
typename = typename std::enable_if<
|
||||
detail::is_duration<To>::value &&
|
||||
!std::chrono::treat_as_floating_point<typename To::rep>::value>::type>
|
||||
constexpr To round(std::chrono::duration<Rep, Period> const& d) {
|
||||
return detail::round_impl(d, floor<To>(d));
|
||||
}
|
||||
|
||||
// mimic: std::chrono::round, C++17
|
||||
// from: http://en.cppreference.com/w/cpp/chrono/time_point/round, CC-BY-SA
|
||||
template <
|
||||
typename To,
|
||||
typename Clock,
|
||||
typename Duration,
|
||||
typename = typename std::enable_if<
|
||||
detail::is_duration<To>::value &&
|
||||
!std::chrono::treat_as_floating_point<typename To::rep>::value>::type>
|
||||
constexpr std::chrono::time_point<Clock, To> round(
|
||||
std::chrono::time_point<Clock, Duration> const& tp) {
|
||||
return std::chrono::time_point<Clock, To>{round<To>(tp.time_since_epoch())};
|
||||
}
|
||||
} // namespace chrono
|
||||
} // namespace folly
|
||||
|
||||
#endif
|
||||
|
||||
namespace folly {
|
||||
namespace chrono {
|
||||
|
||||
struct coarse_steady_clock {
|
||||
using rep = std::chrono::milliseconds::rep;
|
||||
using period = std::chrono::milliseconds::period;
|
||||
using duration = std::chrono::duration<rep, period>;
|
||||
using time_point = std::chrono::time_point<coarse_steady_clock, duration>;
|
||||
constexpr static bool is_steady = true;
|
||||
|
||||
static time_point now() {
|
||||
#ifndef CLOCK_MONOTONIC_COARSE
|
||||
return time_point(std::chrono::duration_cast<duration>(
|
||||
std::chrono::steady_clock::now().time_since_epoch()));
|
||||
#else
|
||||
timespec ts;
|
||||
auto ret = clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
|
||||
if (ret != 0) {
|
||||
throw_exception<std::runtime_error>(
|
||||
"Error using CLOCK_MONOTONIC_COARSE.");
|
||||
}
|
||||
return time_point(std::chrono::duration_cast<duration>(
|
||||
std::chrono::seconds(ts.tv_sec) +
|
||||
std::chrono::nanoseconds(ts.tv_nsec)));
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace chrono
|
||||
} // namespace folly
|
||||
@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <folly/portability/Time.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
namespace folly {
|
||||
namespace chrono {
|
||||
|
||||
extern int (*clock_gettime)(clockid_t, timespec* ts);
|
||||
extern int64_t (*clock_gettime_ns)(clockid_t);
|
||||
} // namespace chrono
|
||||
} // namespace folly
|
||||
@ -1,376 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// @author: Xin Liu <xliux@fb.com>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/random.hpp>
|
||||
#include <boost/type_traits.hpp>
|
||||
#include <glog/logging.h>
|
||||
|
||||
#include <folly/Memory.h>
|
||||
#include <folly/ThreadLocal.h>
|
||||
#include <folly/synchronization/MicroSpinLock.h>
|
||||
|
||||
namespace folly {
|
||||
namespace detail {
|
||||
|
||||
template <typename ValT, typename NodeT>
|
||||
class csl_iterator;
|
||||
|
||||
template <typename T>
|
||||
class SkipListNode : private boost::noncopyable {
|
||||
enum : uint16_t {
|
||||
IS_HEAD_NODE = 1,
|
||||
MARKED_FOR_REMOVAL = (1 << 1),
|
||||
FULLY_LINKED = (1 << 2),
|
||||
};
|
||||
|
||||
public:
|
||||
typedef T value_type;
|
||||
|
||||
template <
|
||||
typename NodeAlloc,
|
||||
typename U,
|
||||
typename =
|
||||
typename std::enable_if<std::is_convertible<U, T>::value>::type>
|
||||
static SkipListNode*
|
||||
create(NodeAlloc& alloc, int height, U&& data, bool isHead = false) {
|
||||
DCHECK(height >= 1 && height < 64) << height;
|
||||
|
||||
size_t size =
|
||||
sizeof(SkipListNode) + height * sizeof(std::atomic<SkipListNode*>);
|
||||
auto storage = std::allocator_traits<NodeAlloc>::allocate(alloc, size);
|
||||
// do placement new
|
||||
return new (storage)
|
||||
SkipListNode(uint8_t(height), std::forward<U>(data), isHead);
|
||||
}
|
||||
|
||||
template <typename NodeAlloc>
|
||||
static void destroy(NodeAlloc& alloc, SkipListNode* node) {
|
||||
size_t size = sizeof(SkipListNode) +
|
||||
node->height_ * sizeof(std::atomic<SkipListNode*>);
|
||||
node->~SkipListNode();
|
||||
std::allocator_traits<NodeAlloc>::deallocate(alloc, node, size);
|
||||
}
|
||||
|
||||
template <typename NodeAlloc>
|
||||
struct DestroyIsNoOp : StrictConjunction<
|
||||
AllocatorHasTrivialDeallocate<NodeAlloc>,
|
||||
boost::has_trivial_destructor<SkipListNode>> {};
|
||||
|
||||
// copy the head node to a new head node assuming lock acquired
|
||||
SkipListNode* copyHead(SkipListNode* node) {
|
||||
DCHECK(node != nullptr && height_ > node->height_);
|
||||
setFlags(node->getFlags());
|
||||
for (uint8_t i = 0; i < node->height_; ++i) {
|
||||
setSkip(i, node->skip(i));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
inline SkipListNode* skip(int layer) const {
|
||||
DCHECK_LT(layer, height_);
|
||||
return skip_[layer].load(std::memory_order_consume);
|
||||
}
|
||||
|
||||
// next valid node as in the linked list
|
||||
SkipListNode* next() {
|
||||
SkipListNode* node;
|
||||
for (node = skip(0); (node != nullptr && node->markedForRemoval());
|
||||
node = node->skip(0)) {
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
void setSkip(uint8_t h, SkipListNode* next) {
|
||||
DCHECK_LT(h, height_);
|
||||
skip_[h].store(next, std::memory_order_release);
|
||||
}
|
||||
|
||||
value_type& data() {
|
||||
return data_;
|
||||
}
|
||||
const value_type& data() const {
|
||||
return data_;
|
||||
}
|
||||
int maxLayer() const {
|
||||
return height_ - 1;
|
||||
}
|
||||
int height() const {
|
||||
return height_;
|
||||
}
|
||||
|
||||
std::unique_lock<MicroSpinLock> acquireGuard() {
|
||||
return std::unique_lock<MicroSpinLock>(spinLock_);
|
||||
}
|
||||
|
||||
bool fullyLinked() const {
|
||||
return getFlags() & FULLY_LINKED;
|
||||
}
|
||||
bool markedForRemoval() const {
|
||||
return getFlags() & MARKED_FOR_REMOVAL;
|
||||
}
|
||||
bool isHeadNode() const {
|
||||
return getFlags() & IS_HEAD_NODE;
|
||||
}
|
||||
|
||||
void setIsHeadNode() {
|
||||
setFlags(uint16_t(getFlags() | IS_HEAD_NODE));
|
||||
}
|
||||
void setFullyLinked() {
|
||||
setFlags(uint16_t(getFlags() | FULLY_LINKED));
|
||||
}
|
||||
void setMarkedForRemoval() {
|
||||
setFlags(uint16_t(getFlags() | MARKED_FOR_REMOVAL));
|
||||
}
|
||||
|
||||
private:
|
||||
// Note! this can only be called from create() as a placement new.
|
||||
template <typename U>
|
||||
SkipListNode(uint8_t height, U&& data, bool isHead)
|
||||
: height_(height), data_(std::forward<U>(data)) {
|
||||
spinLock_.init();
|
||||
setFlags(0);
|
||||
if (isHead) {
|
||||
setIsHeadNode();
|
||||
}
|
||||
// need to explicitly init the dynamic atomic pointer array
|
||||
for (uint8_t i = 0; i < height_; ++i) {
|
||||
new (&skip_[i]) std::atomic<SkipListNode*>(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
~SkipListNode() {
|
||||
for (uint8_t i = 0; i < height_; ++i) {
|
||||
skip_[i].~atomic();
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t getFlags() const {
|
||||
return flags_.load(std::memory_order_consume);
|
||||
}
|
||||
void setFlags(uint16_t flags) {
|
||||
flags_.store(flags, std::memory_order_release);
|
||||
}
|
||||
|
||||
// TODO(xliu): on x86_64, it's possible to squeeze these into
|
||||
// skip_[0] to maybe save 8 bytes depending on the data alignments.
|
||||
// NOTE: currently this is x86_64 only anyway, due to the
|
||||
// MicroSpinLock.
|
||||
std::atomic<uint16_t> flags_;
|
||||
const uint8_t height_;
|
||||
MicroSpinLock spinLock_;
|
||||
|
||||
value_type data_;
|
||||
|
||||
std::atomic<SkipListNode*> skip_[0];
|
||||
};
|
||||
|
||||
class SkipListRandomHeight {
|
||||
enum { kMaxHeight = 64 };
|
||||
|
||||
public:
|
||||
// make it a singleton.
|
||||
static SkipListRandomHeight* instance() {
|
||||
static SkipListRandomHeight instance_;
|
||||
return &instance_;
|
||||
}
|
||||
|
||||
int getHeight(int maxHeight) const {
|
||||
DCHECK_LE(maxHeight, kMaxHeight) << "max height too big!";
|
||||
double p = randomProb();
|
||||
for (int i = 0; i < maxHeight; ++i) {
|
||||
if (p < lookupTable_[i]) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
return maxHeight;
|
||||
}
|
||||
|
||||
size_t getSizeLimit(int height) const {
|
||||
DCHECK_LT(height, kMaxHeight);
|
||||
return sizeLimitTable_[height];
|
||||
}
|
||||
|
||||
private:
|
||||
SkipListRandomHeight() {
|
||||
initLookupTable();
|
||||
}
|
||||
|
||||
void initLookupTable() {
|
||||
// set skip prob = 1/E
|
||||
static const double kProbInv = exp(1);
|
||||
static const double kProb = 1.0 / kProbInv;
|
||||
static const size_t kMaxSizeLimit = std::numeric_limits<size_t>::max();
|
||||
|
||||
double sizeLimit = 1;
|
||||
double p = lookupTable_[0] = (1 - kProb);
|
||||
sizeLimitTable_[0] = 1;
|
||||
for (int i = 1; i < kMaxHeight - 1; ++i) {
|
||||
p *= kProb;
|
||||
sizeLimit *= kProbInv;
|
||||
lookupTable_[i] = lookupTable_[i - 1] + p;
|
||||
sizeLimitTable_[i] = sizeLimit > kMaxSizeLimit
|
||||
? kMaxSizeLimit
|
||||
: static_cast<size_t>(sizeLimit);
|
||||
}
|
||||
lookupTable_[kMaxHeight - 1] = 1;
|
||||
sizeLimitTable_[kMaxHeight - 1] = kMaxSizeLimit;
|
||||
}
|
||||
|
||||
static double randomProb() {
|
||||
static ThreadLocal<boost::lagged_fibonacci2281> rng_;
|
||||
return (*rng_)();
|
||||
}
|
||||
|
||||
double lookupTable_[kMaxHeight];
|
||||
size_t sizeLimitTable_[kMaxHeight];
|
||||
};
|
||||
|
||||
template <typename NodeType, typename NodeAlloc, typename = void>
|
||||
class NodeRecycler;
|
||||
|
||||
template <typename NodeType, typename NodeAlloc>
|
||||
class NodeRecycler<
|
||||
NodeType,
|
||||
NodeAlloc,
|
||||
typename std::enable_if<
|
||||
!NodeType::template DestroyIsNoOp<NodeAlloc>::value>::type> {
|
||||
public:
|
||||
explicit NodeRecycler(const NodeAlloc& alloc)
|
||||
: refs_(0), dirty_(false), alloc_(alloc) {
|
||||
lock_.init();
|
||||
}
|
||||
|
||||
explicit NodeRecycler() : refs_(0), dirty_(false) {
|
||||
lock_.init();
|
||||
}
|
||||
|
||||
~NodeRecycler() {
|
||||
CHECK_EQ(refs(), 0);
|
||||
if (nodes_) {
|
||||
for (auto& node : *nodes_) {
|
||||
NodeType::destroy(alloc_, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void add(NodeType* node) {
|
||||
std::lock_guard<MicroSpinLock> g(lock_);
|
||||
if (nodes_.get() == nullptr) {
|
||||
nodes_ = std::make_unique<std::vector<NodeType*>>(1, node);
|
||||
} else {
|
||||
nodes_->push_back(node);
|
||||
}
|
||||
DCHECK_GT(refs(), 0);
|
||||
dirty_.store(true, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
int addRef() {
|
||||
return refs_.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
int releaseRef() {
|
||||
// We don't expect to clean the recycler immediately everytime it is OK
|
||||
// to do so. Here, it is possible that multiple accessors all release at
|
||||
// the same time but nobody would clean the recycler here. If this
|
||||
// happens, the recycler will usually still get cleaned when
|
||||
// such a race doesn't happen. The worst case is the recycler will
|
||||
// eventually get deleted along with the skiplist.
|
||||
if (LIKELY(!dirty_.load(std::memory_order_relaxed) || refs() > 1)) {
|
||||
return refs_.fetch_add(-1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
std::unique_ptr<std::vector<NodeType*>> newNodes;
|
||||
{
|
||||
std::lock_guard<MicroSpinLock> g(lock_);
|
||||
if (nodes_.get() == nullptr || refs() > 1) {
|
||||
return refs_.fetch_add(-1, std::memory_order_relaxed);
|
||||
}
|
||||
// once refs_ reaches 1 and there is no other accessor, it is safe to
|
||||
// remove all the current nodes in the recycler, as we already acquired
|
||||
// the lock here so no more new nodes can be added, even though new
|
||||
// accessors may be added after that.
|
||||
newNodes.swap(nodes_);
|
||||
dirty_.store(false, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
// TODO(xliu) should we spawn a thread to do this when there are large
|
||||
// number of nodes in the recycler?
|
||||
for (auto& node : *newNodes) {
|
||||
NodeType::destroy(alloc_, node);
|
||||
}
|
||||
|
||||
// decrease the ref count at the very end, to minimize the
|
||||
// chance of other threads acquiring lock_ to clear the deleted
|
||||
// nodes again.
|
||||
return refs_.fetch_add(-1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
NodeAlloc& alloc() {
|
||||
return alloc_;
|
||||
}
|
||||
|
||||
private:
|
||||
int refs() const {
|
||||
return refs_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
std::unique_ptr<std::vector<NodeType*>> nodes_;
|
||||
std::atomic<int32_t> refs_; // current number of visitors to the list
|
||||
std::atomic<bool> dirty_; // whether *nodes_ is non-empty
|
||||
MicroSpinLock lock_; // protects access to *nodes_
|
||||
NodeAlloc alloc_;
|
||||
};
|
||||
|
||||
// In case of arena allocator, no recycling is necessary, and it's possible
|
||||
// to save on ConcurrentSkipList size.
|
||||
template <typename NodeType, typename NodeAlloc>
|
||||
class NodeRecycler<
|
||||
NodeType,
|
||||
NodeAlloc,
|
||||
typename std::enable_if<
|
||||
NodeType::template DestroyIsNoOp<NodeAlloc>::value>::type> {
|
||||
public:
|
||||
explicit NodeRecycler(const NodeAlloc& alloc) : alloc_(alloc) {}
|
||||
|
||||
void addRef() {}
|
||||
void releaseRef() {}
|
||||
|
||||
void add(NodeType* /* node */) {}
|
||||
|
||||
NodeAlloc& alloc() {
|
||||
return alloc_;
|
||||
}
|
||||
|
||||
private:
|
||||
NodeAlloc alloc_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace folly
|
||||
@ -1,421 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
namespace folly {
|
||||
|
||||
// TODO: Replace with std::equal_to, etc., after upgrading to C++14.
|
||||
template <typename T>
|
||||
struct constexpr_equal_to {
|
||||
constexpr bool operator()(T const& a, T const& b) const {
|
||||
return a == b;
|
||||
}
|
||||
};
|
||||
template <typename T>
|
||||
struct constexpr_not_equal_to {
|
||||
constexpr bool operator()(T const& a, T const& b) const {
|
||||
return a != b;
|
||||
}
|
||||
};
|
||||
template <typename T>
|
||||
struct constexpr_less {
|
||||
constexpr bool operator()(T const& a, T const& b) const {
|
||||
return a < b;
|
||||
}
|
||||
};
|
||||
template <typename T>
|
||||
struct constexpr_less_equal {
|
||||
constexpr bool operator()(T const& a, T const& b) const {
|
||||
return a <= b;
|
||||
}
|
||||
};
|
||||
template <typename T>
|
||||
struct constexpr_greater {
|
||||
constexpr bool operator()(T const& a, T const& b) const {
|
||||
return a > b;
|
||||
}
|
||||
};
|
||||
template <typename T>
|
||||
struct constexpr_greater_equal {
|
||||
constexpr bool operator()(T const& a, T const& b) const {
|
||||
return a >= b;
|
||||
}
|
||||
};
|
||||
|
||||
// TLDR: Prefer using operator< for ordering. And when
|
||||
// a and b are equivalent objects, we return b to make
|
||||
// sorting stable.
|
||||
// See http://stepanovpapers.com/notes.pdf for details.
|
||||
template <typename T>
|
||||
constexpr T constexpr_max(T a) {
|
||||
return a;
|
||||
}
|
||||
template <typename T, typename... Ts>
|
||||
constexpr T constexpr_max(T a, T b, Ts... ts) {
|
||||
return b < a ? constexpr_max(a, ts...) : constexpr_max(b, ts...);
|
||||
}
|
||||
|
||||
// When a and b are equivalent objects, we return a to
|
||||
// make sorting stable.
|
||||
template <typename T>
|
||||
constexpr T constexpr_min(T a) {
|
||||
return a;
|
||||
}
|
||||
template <typename T, typename... Ts>
|
||||
constexpr T constexpr_min(T a, T b, Ts... ts) {
|
||||
return b < a ? constexpr_min(b, ts...) : constexpr_min(a, ts...);
|
||||
}
|
||||
|
||||
template <typename T, typename Less>
|
||||
constexpr T const&
|
||||
constexpr_clamp(T const& v, T const& lo, T const& hi, Less less) {
|
||||
return less(v, lo) ? lo : less(hi, v) ? hi : v;
|
||||
}
|
||||
template <typename T>
|
||||
constexpr T const& constexpr_clamp(T const& v, T const& lo, T const& hi) {
|
||||
return constexpr_clamp(v, lo, hi, constexpr_less<T>{});
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct constexpr_abs_helper {};
|
||||
|
||||
template <typename T>
|
||||
struct constexpr_abs_helper<
|
||||
T,
|
||||
typename std::enable_if<std::is_floating_point<T>::value>::type> {
|
||||
static constexpr T go(T t) {
|
||||
return t < static_cast<T>(0) ? -t : t;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct constexpr_abs_helper<
|
||||
T,
|
||||
typename std::enable_if<
|
||||
std::is_integral<T>::value && !std::is_same<T, bool>::value &&
|
||||
std::is_unsigned<T>::value>::type> {
|
||||
static constexpr T go(T t) {
|
||||
return t;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct constexpr_abs_helper<
|
||||
T,
|
||||
typename std::enable_if<
|
||||
std::is_integral<T>::value && !std::is_same<T, bool>::value &&
|
||||
std::is_signed<T>::value>::type> {
|
||||
static constexpr typename std::make_unsigned<T>::type go(T t) {
|
||||
return typename std::make_unsigned<T>::type(t < static_cast<T>(0) ? -t : t);
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <typename T>
|
||||
constexpr auto constexpr_abs(T t)
|
||||
-> decltype(detail::constexpr_abs_helper<T>::go(t)) {
|
||||
return detail::constexpr_abs_helper<T>::go(t);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template <typename T>
|
||||
constexpr T constexpr_log2_(T a, T e) {
|
||||
return e == T(1) ? a : constexpr_log2_(a + T(1), e / T(2));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T constexpr_log2_ceil_(T l2, T t) {
|
||||
return l2 + T(T(1) << l2 < t ? 1 : 0);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T constexpr_square_(T t) {
|
||||
return t * t;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template <typename T>
|
||||
constexpr T constexpr_log2(T t) {
|
||||
return detail::constexpr_log2_(T(0), t);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T constexpr_log2_ceil(T t) {
|
||||
return detail::constexpr_log2_ceil_(constexpr_log2(t), t);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T constexpr_ceil(T t, T round) {
|
||||
return round == T(0)
|
||||
? t
|
||||
: ((t + (t < T(0) ? T(0) : round - T(1))) / round) * round;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T constexpr_pow(T base, std::size_t exp) {
|
||||
return exp == 0
|
||||
? T(1)
|
||||
: exp == 1 ? base
|
||||
: detail::constexpr_square_(constexpr_pow(base, exp / 2)) *
|
||||
(exp % 2 ? base : T(1));
|
||||
}
|
||||
|
||||
/// constexpr_find_last_set
|
||||
///
|
||||
/// Return the 1-based index of the most significant bit which is set.
|
||||
/// For x > 0, constexpr_find_last_set(x) == 1 + floor(log2(x)).
|
||||
template <typename T>
|
||||
constexpr std::size_t constexpr_find_last_set(T const t) {
|
||||
using U = std::make_unsigned_t<T>;
|
||||
return t == T(0) ? 0 : 1 + constexpr_log2(static_cast<U>(t));
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template <typename U>
|
||||
constexpr std::size_t
|
||||
constexpr_find_first_set_(std::size_t s, std::size_t a, U const u) {
|
||||
return s == 0 ? a
|
||||
: constexpr_find_first_set_(
|
||||
s / 2, a + s * bool((u >> a) % (U(1) << s) == U(0)), u);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
/// constexpr_find_first_set
|
||||
///
|
||||
/// Return the 1-based index of the least significant bit which is set.
|
||||
/// For x > 0, the exponent in the largest power of two which does not divide x.
|
||||
template <typename T>
|
||||
constexpr std::size_t constexpr_find_first_set(T t) {
|
||||
using U = std::make_unsigned_t<T>;
|
||||
using size = std::integral_constant<std::size_t, sizeof(T) * 4>;
|
||||
return t == T(0)
|
||||
? 0
|
||||
: 1 + detail::constexpr_find_first_set_(size{}, 0, static_cast<U>(t));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T constexpr_add_overflow_clamped(T a, T b) {
|
||||
using L = std::numeric_limits<T>;
|
||||
using M = std::intmax_t;
|
||||
static_assert(
|
||||
!std::is_integral<T>::value || sizeof(T) <= sizeof(M),
|
||||
"Integral type too large!");
|
||||
// clang-format off
|
||||
return
|
||||
// don't do anything special for non-integral types.
|
||||
!std::is_integral<T>::value ? a + b :
|
||||
// for narrow integral types, just convert to intmax_t.
|
||||
sizeof(T) < sizeof(M)
|
||||
? T(constexpr_clamp(M(a) + M(b), M(L::min()), M(L::max()))) :
|
||||
// when a >= 0, cannot add more than `MAX - a` onto a.
|
||||
!(a < 0) ? a + constexpr_min(b, T(L::max() - a)) :
|
||||
// a < 0 && b >= 0, `a + b` will always be in valid range of type T.
|
||||
!(b < 0) ? a + b :
|
||||
// a < 0 && b < 0, keep the result >= MIN.
|
||||
a + constexpr_max(b, T(L::min() - a));
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T constexpr_sub_overflow_clamped(T a, T b) {
|
||||
using L = std::numeric_limits<T>;
|
||||
using M = std::intmax_t;
|
||||
static_assert(
|
||||
!std::is_integral<T>::value || sizeof(T) <= sizeof(M),
|
||||
"Integral type too large!");
|
||||
// clang-format off
|
||||
return
|
||||
// don't do anything special for non-integral types.
|
||||
!std::is_integral<T>::value ? a - b :
|
||||
// for unsigned type, keep result >= 0.
|
||||
std::is_unsigned<T>::value ? (a < b ? 0 : a - b) :
|
||||
// for narrow signed integral types, just convert to intmax_t.
|
||||
sizeof(T) < sizeof(M)
|
||||
? T(constexpr_clamp(M(a) - M(b), M(L::min()), M(L::max()))) :
|
||||
// (a >= 0 && b >= 0) || (a < 0 && b < 0), `a - b` will always be valid.
|
||||
(a < 0) == (b < 0) ? a - b :
|
||||
// MIN < b, so `-b` should be in valid range (-MAX <= -b <= MAX),
|
||||
// convert subtraction to addition.
|
||||
L::min() < b ? constexpr_add_overflow_clamped(a, T(-b)) :
|
||||
// -b = -MIN = (MAX + 1) and a <= -1, result is in valid range.
|
||||
a < 0 ? a - b :
|
||||
// -b = -MIN = (MAX + 1) and a >= 0, result > MAX.
|
||||
L::max();
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
// clamp_cast<> provides sane numeric conversions from float point numbers to
|
||||
// integral numbers, and between different types of integral numbers. It helps
|
||||
// to avoid unexpected bugs introduced by bad conversion, and undefined behavior
|
||||
// like overflow when casting float point numbers to integral numbers.
|
||||
//
|
||||
// When doing clamp_cast<Dst>(value), if `value` is in valid range of Dst,
|
||||
// it will give correct result in Dst, equal to `value`.
|
||||
//
|
||||
// If `value` is outside the representable range of Dst, it will be clamped to
|
||||
// MAX or MIN in Dst, instead of being undefined behavior.
|
||||
//
|
||||
// Float NaNs are converted to 0 in integral type.
|
||||
//
|
||||
// Here's some comparision with static_cast<>:
|
||||
// (with FB-internal gcc-5-glibc-2.23 toolchain)
|
||||
//
|
||||
// static_cast<int32_t>(NaN) = 6
|
||||
// clamp_cast<int32_t>(NaN) = 0
|
||||
//
|
||||
// static_cast<int32_t>(9999999999.0f) = -348639895
|
||||
// clamp_cast<int32_t>(9999999999.0f) = 2147483647
|
||||
//
|
||||
// static_cast<int32_t>(2147483647.0f) = -348639895
|
||||
// clamp_cast<int32_t>(2147483647.0f) = 2147483647
|
||||
//
|
||||
// static_cast<uint32_t>(4294967295.0f) = 0
|
||||
// clamp_cast<uint32_t>(4294967295.0f) = 4294967295
|
||||
//
|
||||
// static_cast<uint32_t>(-1) = 4294967295
|
||||
// clamp_cast<uint32_t>(-1) = 0
|
||||
//
|
||||
// static_cast<int16_t>(32768u) = -32768
|
||||
// clamp_cast<int16_t>(32768u) = 32767
|
||||
|
||||
template <typename Dst, typename Src>
|
||||
constexpr typename std::enable_if<std::is_integral<Src>::value, Dst>::type
|
||||
constexpr_clamp_cast(Src src) {
|
||||
static_assert(
|
||||
std::is_integral<Dst>::value && sizeof(Dst) <= sizeof(int64_t),
|
||||
"constexpr_clamp_cast can only cast into integral type (up to 64bit)");
|
||||
|
||||
using L = std::numeric_limits<Dst>;
|
||||
// clang-format off
|
||||
return
|
||||
// Check if Src and Dst have same signedness.
|
||||
std::is_signed<Src>::value == std::is_signed<Dst>::value
|
||||
? (
|
||||
// Src and Dst have same signedness. If sizeof(Src) <= sizeof(Dst),
|
||||
// we can safely convert Src to Dst without any loss of accuracy.
|
||||
sizeof(Src) <= sizeof(Dst) ? Dst(src) :
|
||||
// If Src is larger in size, we need to clamp it to valid range in Dst.
|
||||
Dst(constexpr_clamp(src, Src(L::min()), Src(L::max()))))
|
||||
// Src and Dst have different signedness.
|
||||
// Check if it's signed -> unsigend cast.
|
||||
: std::is_signed<Src>::value && std::is_unsigned<Dst>::value
|
||||
? (
|
||||
// If src < 0, the result should be 0.
|
||||
src < 0 ? Dst(0) :
|
||||
// Otherwise, src >= 0. If src can fit into Dst, we can safely cast it
|
||||
// without loss of accuracy.
|
||||
sizeof(Src) <= sizeof(Dst) ? Dst(src) :
|
||||
// If Src is larger in size than Dst, we need to ensure the result is
|
||||
// at most Dst MAX.
|
||||
Dst(constexpr_min(src, Src(L::max()))))
|
||||
// It's unsigned -> signed cast.
|
||||
: (
|
||||
// Since Src is unsigned, and Dst is signed, Src can fit into Dst only
|
||||
// when sizeof(Src) < sizeof(Dst).
|
||||
sizeof(Src) < sizeof(Dst) ? Dst(src) :
|
||||
// If Src does not fit into Dst, we need to ensure the result is at most
|
||||
// Dst MAX.
|
||||
Dst(constexpr_min(src, Src(L::max()))));
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
// Upper/lower bound values that could be accurately represented in both
|
||||
// integral and float point types.
|
||||
constexpr double kClampCastLowerBoundDoubleToInt64F = -9223372036854774784.0;
|
||||
constexpr double kClampCastUpperBoundDoubleToInt64F = 9223372036854774784.0;
|
||||
constexpr double kClampCastUpperBoundDoubleToUInt64F = 18446744073709549568.0;
|
||||
|
||||
constexpr float kClampCastLowerBoundFloatToInt32F = -2147483520.0f;
|
||||
constexpr float kClampCastUpperBoundFloatToInt32F = 2147483520.0f;
|
||||
constexpr float kClampCastUpperBoundFloatToUInt32F = 4294967040.0f;
|
||||
|
||||
// This works the same as constexpr_clamp, but the comparision are done in Src
|
||||
// to prevent any implicit promotions.
|
||||
template <typename D, typename S>
|
||||
constexpr D constexpr_clamp_cast_helper(S src, S sl, S su, D dl, D du) {
|
||||
return src < sl ? dl : (src > su ? du : D(src));
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template <typename Dst, typename Src>
|
||||
constexpr typename std::enable_if<std::is_floating_point<Src>::value, Dst>::type
|
||||
constexpr_clamp_cast(Src src) {
|
||||
static_assert(
|
||||
std::is_integral<Dst>::value && sizeof(Dst) <= sizeof(int64_t),
|
||||
"constexpr_clamp_cast can only cast into integral type (up to 64bit)");
|
||||
|
||||
using L = std::numeric_limits<Dst>;
|
||||
// clang-format off
|
||||
return
|
||||
// Special case: cast NaN into 0.
|
||||
// Using a trick here to portably check for NaN: f != f only if f is NaN.
|
||||
// see: https://stackoverflow.com/a/570694
|
||||
(src != src) ? Dst(0) :
|
||||
// using `sizeof(Src) > sizeof(Dst)` as a heuristic that Dst can be
|
||||
// represented in Src without loss of accuracy.
|
||||
// see: https://en.wikipedia.org/wiki/Floating-point_arithmetic
|
||||
sizeof(Src) > sizeof(Dst) ?
|
||||
detail::constexpr_clamp_cast_helper(
|
||||
src, Src(L::min()), Src(L::max()), L::min(), L::max()) :
|
||||
// sizeof(Src) < sizeof(Dst) only happens when doing cast of
|
||||
// 32bit float -> u/int64_t.
|
||||
// Losslessly promote float into double, change into double -> u/int64_t.
|
||||
sizeof(Src) < sizeof(Dst) ? (
|
||||
src >= 0.0
|
||||
? constexpr_clamp_cast<Dst>(
|
||||
constexpr_clamp_cast<std::uint64_t>(double(src)))
|
||||
: constexpr_clamp_cast<Dst>(
|
||||
constexpr_clamp_cast<std::int64_t>(double(src)))) :
|
||||
// The following are for sizeof(Src) == sizeof(Dst).
|
||||
std::is_same<Src, double>::value && std::is_same<Dst, int64_t>::value ?
|
||||
detail::constexpr_clamp_cast_helper(
|
||||
double(src),
|
||||
detail::kClampCastLowerBoundDoubleToInt64F,
|
||||
detail::kClampCastUpperBoundDoubleToInt64F,
|
||||
L::min(),
|
||||
L::max()) :
|
||||
std::is_same<Src, double>::value && std::is_same<Dst, uint64_t>::value ?
|
||||
detail::constexpr_clamp_cast_helper(
|
||||
double(src),
|
||||
0.0,
|
||||
detail::kClampCastUpperBoundDoubleToUInt64F,
|
||||
L::min(),
|
||||
L::max()) :
|
||||
std::is_same<Src, float>::value && std::is_same<Dst, int32_t>::value ?
|
||||
detail::constexpr_clamp_cast_helper(
|
||||
float(src),
|
||||
detail::kClampCastLowerBoundFloatToInt32F,
|
||||
detail::kClampCastUpperBoundFloatToInt32F,
|
||||
L::min(),
|
||||
L::max()) :
|
||||
detail::constexpr_clamp_cast_helper(
|
||||
float(src),
|
||||
0.0f,
|
||||
detail::kClampCastUpperBoundFloatToUInt32F,
|
||||
L::min(),
|
||||
L::max());
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
} // namespace folly
|
||||
1586
openvidu-react-native-androidx/ios/Pods/Folly/folly/Conv.h
generated
@ -1,121 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* GCC compatible wrappers around clang attributes.
|
||||
*
|
||||
* @author Dominik Gabi
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __has_attribute
|
||||
#define FOLLY_HAS_ATTRIBUTE(x) 0
|
||||
#else
|
||||
#define FOLLY_HAS_ATTRIBUTE(x) __has_attribute(x)
|
||||
#endif
|
||||
|
||||
#ifndef __has_cpp_attribute
|
||||
#define FOLLY_HAS_CPP_ATTRIBUTE(x) 0
|
||||
#else
|
||||
#define FOLLY_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
|
||||
#endif
|
||||
|
||||
#ifndef __has_extension
|
||||
#define FOLLY_HAS_EXTENSION(x) 0
|
||||
#else
|
||||
#define FOLLY_HAS_EXTENSION(x) __has_extension(x)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Fallthrough to indicate that `break` was left out on purpose in a switch
|
||||
* statement, e.g.
|
||||
*
|
||||
* switch (n) {
|
||||
* case 22:
|
||||
* case 33: // no warning: no statements between case labels
|
||||
* f();
|
||||
* case 44: // warning: unannotated fall-through
|
||||
* g();
|
||||
* FOLLY_FALLTHROUGH; // no warning: annotated fall-through
|
||||
* }
|
||||
*/
|
||||
#if FOLLY_HAS_CPP_ATTRIBUTE(fallthrough)
|
||||
#define FOLLY_FALLTHROUGH [[fallthrough]]
|
||||
#elif FOLLY_HAS_CPP_ATTRIBUTE(clang::fallthrough)
|
||||
#define FOLLY_FALLTHROUGH [[clang::fallthrough]]
|
||||
#elif FOLLY_HAS_CPP_ATTRIBUTE(gnu::fallthrough)
|
||||
#define FOLLY_FALLTHROUGH [[gnu::fallthrough]]
|
||||
#else
|
||||
#define FOLLY_FALLTHROUGH
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Maybe_unused indicates that a function, variable or parameter might or
|
||||
* might not be used, e.g.
|
||||
*
|
||||
* int foo(FOLLY_MAYBE_UNUSED int x) {
|
||||
* #ifdef USE_X
|
||||
* return x;
|
||||
* #else
|
||||
* return 0;
|
||||
* #endif
|
||||
* }
|
||||
*/
|
||||
#if FOLLY_HAS_CPP_ATTRIBUTE(maybe_unused)
|
||||
#define FOLLY_MAYBE_UNUSED [[maybe_unused]]
|
||||
#elif FOLLY_HAS_ATTRIBUTE(__unused__) || __GNUC__
|
||||
#define FOLLY_MAYBE_UNUSED __attribute__((__unused__))
|
||||
#else
|
||||
#define FOLLY_MAYBE_UNUSED
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Nullable indicates that a return value or a parameter may be a `nullptr`,
|
||||
* e.g.
|
||||
*
|
||||
* int* FOLLY_NULLABLE foo(int* a, int* FOLLY_NULLABLE b) {
|
||||
* if (*a > 0) { // safe dereference
|
||||
* return nullptr;
|
||||
* }
|
||||
* if (*b < 0) { // unsafe dereference
|
||||
* return *a;
|
||||
* }
|
||||
* if (b != nullptr && *b == 1) { // safe checked dereference
|
||||
* return new int(1);
|
||||
* }
|
||||
* return nullptr;
|
||||
* }
|
||||
*/
|
||||
#if FOLLY_HAS_EXTENSION(nullability)
|
||||
#define FOLLY_NULLABLE _Nullable
|
||||
#define FOLLY_NONNULL _Nonnull
|
||||
#else
|
||||
#define FOLLY_NULLABLE
|
||||
#define FOLLY_NONNULL
|
||||
#endif
|
||||
|
||||
/**
|
||||
* "Cold" indicates to the compiler that a function is only expected to be
|
||||
* called from unlikely code paths. It can affect decisions made by the
|
||||
* optimizer both when processing the function body and when analyzing
|
||||
* call-sites.
|
||||
*/
|
||||
#if __GNUC__
|
||||
#define FOLLY_COLD __attribute__((__cold__))
|
||||
#else
|
||||
#define FOLLY_COLD
|
||||
#endif
|
||||
@ -1,218 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <folly/Portability.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
namespace folly {
|
||||
|
||||
/**
|
||||
* Identification of an Intel CPU.
|
||||
* Supports CPUID feature flags (EAX=1) and extended features (EAX=7, ECX=0).
|
||||
* Values from
|
||||
* http://www.intel.com/content/www/us/en/processors/processor-identification-cpuid-instruction-note.html
|
||||
*/
|
||||
class CpuId {
|
||||
public:
|
||||
// Always inline in order for this to be usable from a __ifunc__.
|
||||
// In shared library mode, a __ifunc__ runs at relocation time, while the
|
||||
// PLT hasn't been fully populated yet; thus, ifuncs cannot use symbols
|
||||
// with potentially external linkage. (This issue is less likely in opt
|
||||
// mode since inlining happens more likely, and it doesn't happen for
|
||||
// statically linked binaries which don't depend on the PLT)
|
||||
FOLLY_ALWAYS_INLINE CpuId() {
|
||||
#if defined(_MSC_VER) && (FOLLY_X64 || defined(_M_IX86))
|
||||
int reg[4];
|
||||
__cpuid(static_cast<int*>(reg), 0);
|
||||
const int n = reg[0];
|
||||
if (n >= 1) {
|
||||
__cpuid(static_cast<int*>(reg), 1);
|
||||
f1c_ = uint32_t(reg[2]);
|
||||
f1d_ = uint32_t(reg[3]);
|
||||
}
|
||||
if (n >= 7) {
|
||||
__cpuidex(static_cast<int*>(reg), 7, 0);
|
||||
f7b_ = uint32_t(reg[1]);
|
||||
f7c_ = uint32_t(reg[2]);
|
||||
}
|
||||
#elif defined(__i386__) && defined(__PIC__) && !defined(__clang__) && \
|
||||
defined(__GNUC__)
|
||||
// The following block like the normal cpuid branch below, but gcc
|
||||
// reserves ebx for use of its pic register so we must specially
|
||||
// handle the save and restore to avoid clobbering the register
|
||||
uint32_t n;
|
||||
__asm__(
|
||||
"pushl %%ebx\n\t"
|
||||
"cpuid\n\t"
|
||||
"popl %%ebx\n\t"
|
||||
: "=a"(n)
|
||||
: "a"(0)
|
||||
: "ecx", "edx");
|
||||
if (n >= 1) {
|
||||
uint32_t f1a;
|
||||
__asm__(
|
||||
"pushl %%ebx\n\t"
|
||||
"cpuid\n\t"
|
||||
"popl %%ebx\n\t"
|
||||
: "=a"(f1a), "=c"(f1c_), "=d"(f1d_)
|
||||
: "a"(1)
|
||||
:);
|
||||
}
|
||||
if (n >= 7) {
|
||||
__asm__(
|
||||
"pushl %%ebx\n\t"
|
||||
"cpuid\n\t"
|
||||
"movl %%ebx, %%eax\n\r"
|
||||
"popl %%ebx"
|
||||
: "=a"(f7b_), "=c"(f7c_)
|
||||
: "a"(7), "c"(0)
|
||||
: "edx");
|
||||
}
|
||||
#elif FOLLY_X64 || defined(__i386__)
|
||||
uint32_t n;
|
||||
__asm__("cpuid" : "=a"(n) : "a"(0) : "ebx", "ecx", "edx");
|
||||
if (n >= 1) {
|
||||
uint32_t f1a;
|
||||
__asm__("cpuid" : "=a"(f1a), "=c"(f1c_), "=d"(f1d_) : "a"(1) : "ebx");
|
||||
}
|
||||
if (n >= 7) {
|
||||
uint32_t f7a;
|
||||
__asm__("cpuid"
|
||||
: "=a"(f7a), "=b"(f7b_), "=c"(f7c_)
|
||||
: "a"(7), "c"(0)
|
||||
: "edx");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#define X(name, r, bit) \
|
||||
FOLLY_ALWAYS_INLINE bool name() const { \
|
||||
return ((r) & (1U << bit)) != 0; \
|
||||
}
|
||||
|
||||
// cpuid(1): Processor Info and Feature Bits.
|
||||
#define C(name, bit) X(name, f1c_, bit)
|
||||
C(sse3, 0)
|
||||
C(pclmuldq, 1)
|
||||
C(dtes64, 2)
|
||||
C(monitor, 3)
|
||||
C(dscpl, 4)
|
||||
C(vmx, 5)
|
||||
C(smx, 6)
|
||||
C(eist, 7)
|
||||
C(tm2, 8)
|
||||
C(ssse3, 9)
|
||||
C(cnxtid, 10)
|
||||
C(fma, 12)
|
||||
C(cx16, 13)
|
||||
C(xtpr, 14)
|
||||
C(pdcm, 15)
|
||||
C(pcid, 17)
|
||||
C(dca, 18)
|
||||
C(sse41, 19)
|
||||
C(sse42, 20)
|
||||
C(x2apic, 21)
|
||||
C(movbe, 22)
|
||||
C(popcnt, 23)
|
||||
C(tscdeadline, 24)
|
||||
C(aes, 25)
|
||||
C(xsave, 26)
|
||||
C(osxsave, 27)
|
||||
C(avx, 28)
|
||||
C(f16c, 29)
|
||||
C(rdrand, 30)
|
||||
#undef C
|
||||
#define D(name, bit) X(name, f1d_, bit)
|
||||
D(fpu, 0)
|
||||
D(vme, 1)
|
||||
D(de, 2)
|
||||
D(pse, 3)
|
||||
D(tsc, 4)
|
||||
D(msr, 5)
|
||||
D(pae, 6)
|
||||
D(mce, 7)
|
||||
D(cx8, 8)
|
||||
D(apic, 9)
|
||||
D(sep, 11)
|
||||
D(mtrr, 12)
|
||||
D(pge, 13)
|
||||
D(mca, 14)
|
||||
D(cmov, 15)
|
||||
D(pat, 16)
|
||||
D(pse36, 17)
|
||||
D(psn, 18)
|
||||
D(clfsh, 19)
|
||||
D(ds, 21)
|
||||
D(acpi, 22)
|
||||
D(mmx, 23)
|
||||
D(fxsr, 24)
|
||||
D(sse, 25)
|
||||
D(sse2, 26)
|
||||
D(ss, 27)
|
||||
D(htt, 28)
|
||||
D(tm, 29)
|
||||
D(pbe, 31)
|
||||
#undef D
|
||||
|
||||
// cpuid(7): Extended Features.
|
||||
#define B(name, bit) X(name, f7b_, bit)
|
||||
B(bmi1, 3)
|
||||
B(hle, 4)
|
||||
B(avx2, 5)
|
||||
B(smep, 7)
|
||||
B(bmi2, 8)
|
||||
B(erms, 9)
|
||||
B(invpcid, 10)
|
||||
B(rtm, 11)
|
||||
B(mpx, 14)
|
||||
B(avx512f, 16)
|
||||
B(avx512dq, 17)
|
||||
B(rdseed, 18)
|
||||
B(adx, 19)
|
||||
B(smap, 20)
|
||||
B(avx512ifma, 21)
|
||||
B(pcommit, 22)
|
||||
B(clflushopt, 23)
|
||||
B(clwb, 24)
|
||||
B(avx512pf, 26)
|
||||
B(avx512er, 27)
|
||||
B(avx512cd, 28)
|
||||
B(sha, 29)
|
||||
B(avx512bw, 30)
|
||||
B(avx512vl, 31)
|
||||
#undef B
|
||||
#define C(name, bit) X(name, f7c_, bit)
|
||||
C(prefetchwt1, 0)
|
||||
C(avx512vbmi, 1)
|
||||
#undef C
|
||||
|
||||
#undef X
|
||||
|
||||
private:
|
||||
uint32_t f1c_ = 0;
|
||||
uint32_t f1d_ = 0;
|
||||
uint32_t f7b_ = 0;
|
||||
uint32_t f7c_ = 0;
|
||||
};
|
||||
|
||||
} // namespace folly
|
||||
@ -1,150 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <future>
|
||||
|
||||
#include <glog/logging.h>
|
||||
|
||||
#include <folly/Executor.h>
|
||||
#include <folly/synchronization/Baton.h>
|
||||
|
||||
namespace folly {
|
||||
|
||||
/// An Executor accepts units of work with add(), which should be
|
||||
/// threadsafe.
|
||||
class DefaultKeepAliveExecutor : public virtual Executor {
|
||||
public:
|
||||
DefaultKeepAliveExecutor() : Executor() {}
|
||||
|
||||
virtual ~DefaultKeepAliveExecutor() {
|
||||
DCHECK(!keepAlive_);
|
||||
}
|
||||
|
||||
folly::Executor::KeepAlive<> weakRef() {
|
||||
return WeakRef::create(controlBlock_, this);
|
||||
}
|
||||
|
||||
protected:
|
||||
void joinKeepAlive() {
|
||||
DCHECK(keepAlive_);
|
||||
keepAlive_.reset();
|
||||
keepAliveReleaseBaton_.wait();
|
||||
}
|
||||
|
||||
private:
|
||||
struct ControlBlock {
|
||||
std::atomic<ssize_t> keepAliveCount_{1};
|
||||
};
|
||||
|
||||
class WeakRef : public Executor {
|
||||
public:
|
||||
static folly::Executor::KeepAlive<> create(
|
||||
std::shared_ptr<ControlBlock> controlBlock,
|
||||
Executor* executor) {
|
||||
return makeKeepAlive(new WeakRef(std::move(controlBlock), executor));
|
||||
}
|
||||
|
||||
void add(Func f) override {
|
||||
if (auto executor = lock()) {
|
||||
executor->add(std::move(f));
|
||||
}
|
||||
}
|
||||
|
||||
void addWithPriority(Func f, int8_t priority) override {
|
||||
if (auto executor = lock()) {
|
||||
executor->addWithPriority(std::move(f), priority);
|
||||
}
|
||||
}
|
||||
|
||||
virtual uint8_t getNumPriorities() const override {
|
||||
return numPriorities_;
|
||||
}
|
||||
|
||||
private:
|
||||
WeakRef(std::shared_ptr<ControlBlock> controlBlock, Executor* executor)
|
||||
: controlBlock_(std::move(controlBlock)),
|
||||
executor_(executor),
|
||||
numPriorities_(executor->getNumPriorities()) {}
|
||||
|
||||
bool keepAliveAcquire() override {
|
||||
auto keepAliveCount =
|
||||
keepAliveCount_.fetch_add(1, std::memory_order_relaxed);
|
||||
// We should never increment from 0
|
||||
DCHECK(keepAliveCount > 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void keepAliveRelease() override {
|
||||
auto keepAliveCount =
|
||||
keepAliveCount_.fetch_sub(1, std::memory_order_acq_rel);
|
||||
DCHECK(keepAliveCount >= 1);
|
||||
|
||||
if (keepAliveCount == 1) {
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
folly::Executor::KeepAlive<> lock() {
|
||||
auto controlBlock =
|
||||
controlBlock_->keepAliveCount_.load(std::memory_order_relaxed);
|
||||
do {
|
||||
if (controlBlock == 0) {
|
||||
return {};
|
||||
}
|
||||
} while (!controlBlock_->keepAliveCount_.compare_exchange_weak(
|
||||
controlBlock,
|
||||
controlBlock + 1,
|
||||
std::memory_order_release,
|
||||
std::memory_order_relaxed));
|
||||
|
||||
return makeKeepAlive(executor_);
|
||||
}
|
||||
|
||||
std::atomic<size_t> keepAliveCount_{1};
|
||||
|
||||
std::shared_ptr<ControlBlock> controlBlock_;
|
||||
Executor* executor_;
|
||||
|
||||
uint8_t numPriorities_;
|
||||
};
|
||||
|
||||
bool keepAliveAcquire() override {
|
||||
auto keepAliveCount =
|
||||
controlBlock_->keepAliveCount_.fetch_add(1, std::memory_order_relaxed);
|
||||
// We should never increment from 0
|
||||
DCHECK(keepAliveCount > 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void keepAliveRelease() override {
|
||||
auto keepAliveCount =
|
||||
controlBlock_->keepAliveCount_.fetch_sub(1, std::memory_order_acquire);
|
||||
DCHECK(keepAliveCount >= 1);
|
||||
|
||||
if (keepAliveCount == 1) {
|
||||
keepAliveReleaseBaton_.post(); // std::memory_order_release
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<ControlBlock> controlBlock_{std::make_shared<ControlBlock>()};
|
||||
Baton<> keepAliveReleaseBaton_;
|
||||
KeepAlive<DefaultKeepAliveExecutor> keepAlive_{
|
||||
makeKeepAlive<DefaultKeepAliveExecutor>(this)};
|
||||
};
|
||||
|
||||
} // namespace folly
|
||||
@ -1,131 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <folly/Demangle.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#include <folly/detail/Demangle.h>
|
||||
#include <folly/portability/Config.h>
|
||||
|
||||
#if FOLLY_DETAIL_HAVE_DEMANGLE_H
|
||||
|
||||
#include <cxxabi.h>
|
||||
|
||||
#endif
|
||||
|
||||
namespace folly {
|
||||
|
||||
#if FOLLY_DETAIL_HAVE_DEMANGLE_H
|
||||
|
||||
fbstring demangle(const char* name) {
|
||||
#ifdef FOLLY_DEMANGLE_MAX_SYMBOL_SIZE
|
||||
// GCC's __cxa_demangle() uses on-stack data structures for the
|
||||
// parser state which are linear in the number of components of the
|
||||
// symbol. For extremely long symbols, this can cause a stack
|
||||
// overflow. We set an arbitrary symbol length limit above which we
|
||||
// just return the mangled name.
|
||||
size_t mangledLen = strlen(name);
|
||||
if (mangledLen > FOLLY_DEMANGLE_MAX_SYMBOL_SIZE) {
|
||||
return fbstring(name, mangledLen);
|
||||
}
|
||||
#endif
|
||||
|
||||
int status;
|
||||
size_t len = 0;
|
||||
// malloc() memory for the demangled type name
|
||||
char* demangled = abi::__cxa_demangle(name, nullptr, &len, &status);
|
||||
if (status != 0) {
|
||||
return name;
|
||||
}
|
||||
// len is the length of the buffer (including NUL terminator and maybe
|
||||
// other junk)
|
||||
return fbstring(demangled, strlen(demangled), len, AcquireMallocatedString());
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
struct DemangleBuf {
|
||||
char* dest;
|
||||
size_t remaining;
|
||||
size_t total;
|
||||
};
|
||||
|
||||
void demangleCallback(const char* str, size_t size, void* p) {
|
||||
DemangleBuf* buf = static_cast<DemangleBuf*>(p);
|
||||
size_t n = std::min(buf->remaining, size);
|
||||
memcpy(buf->dest, str, n);
|
||||
buf->dest += n;
|
||||
buf->remaining -= n;
|
||||
buf->total += size;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
size_t demangle(const char* name, char* out, size_t outSize) {
|
||||
#ifdef FOLLY_DEMANGLE_MAX_SYMBOL_SIZE
|
||||
size_t mangledLen = strlen(name);
|
||||
if (mangledLen > FOLLY_DEMANGLE_MAX_SYMBOL_SIZE) {
|
||||
if (outSize) {
|
||||
size_t n = std::min(mangledLen, outSize - 1);
|
||||
memcpy(out, name, n);
|
||||
out[n] = '\0';
|
||||
}
|
||||
return mangledLen;
|
||||
}
|
||||
#endif
|
||||
|
||||
DemangleBuf dbuf;
|
||||
dbuf.dest = out;
|
||||
dbuf.remaining = outSize ? outSize - 1 : 0; // leave room for null term
|
||||
dbuf.total = 0;
|
||||
|
||||
// Unlike most library functions, this returns 1 on success and 0 on failure
|
||||
int status =
|
||||
detail::cplus_demangle_v3_callback_wrapper(name, demangleCallback, &dbuf);
|
||||
if (status == 0) { // failed, return original
|
||||
return folly::strlcpy(out, name, outSize);
|
||||
}
|
||||
if (outSize != 0) {
|
||||
*dbuf.dest = '\0';
|
||||
}
|
||||
return dbuf.total;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
fbstring demangle(const char* name) {
|
||||
return name;
|
||||
}
|
||||
|
||||
size_t demangle(const char* name, char* out, size_t outSize) {
|
||||
return folly::strlcpy(out, name, outSize);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
size_t strlcpy(char* dest, const char* const src, size_t size) {
|
||||
size_t len = strlen(src);
|
||||
if (size != 0) {
|
||||
size_t n = std::min(len, size - 1); // always null terminate!
|
||||
memcpy(dest, src, n);
|
||||
dest[n] = '\0';
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
} // namespace folly
|
||||
@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <folly/FBString.h>
|
||||
|
||||
namespace folly {
|
||||
|
||||
/**
|
||||
* Return the demangled (prettyfied) version of a C++ type.
|
||||
*
|
||||
* This function tries to produce a human-readable type, but the type name will
|
||||
* be returned unchanged in case of error or if demangling isn't supported on
|
||||
* your system.
|
||||
*
|
||||
* Use for debugging -- do not rely on demangle() returning anything useful.
|
||||
*
|
||||
* This function may allocate memory (and therefore throw std::bad_alloc).
|
||||
*/
|
||||
fbstring demangle(const char* name);
|
||||
inline fbstring demangle(const std::type_info& type) {
|
||||
return demangle(type.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the demangled (prettyfied) version of a C++ type in a user-provided
|
||||
* buffer.
|
||||
*
|
||||
* The semantics are the same as for snprintf or strlcpy: bufSize is the size
|
||||
* of the buffer, the string is always null-terminated, and the return value is
|
||||
* the number of characters (not including the null terminator) that would have
|
||||
* been written if the buffer was big enough. (So a return value >= bufSize
|
||||
* indicates that the output was truncated)
|
||||
*
|
||||
* This function does not allocate memory and is async-signal-safe.
|
||||
*
|
||||
* Note that the underlying function for the fbstring-returning demangle is
|
||||
* somewhat standard (abi::__cxa_demangle, which uses malloc), the underlying
|
||||
* function for this version is less so (cplus_demangle_v3_callback from
|
||||
* libiberty), so it is possible for the fbstring version to work, while this
|
||||
* version returns the original, mangled name.
|
||||
*/
|
||||
size_t demangle(const char* name, char* buf, size_t bufSize);
|
||||
inline size_t demangle(const std::type_info& type, char* buf, size_t bufSize) {
|
||||
return demangle(type.name(), buf, bufSize);
|
||||
}
|
||||
|
||||
// glibc doesn't have strlcpy
|
||||
size_t strlcpy(char* dest, const char* const src, size_t size);
|
||||
|
||||
} // namespace folly
|
||||
@ -1,247 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Discriminated pointer: Type-safe pointer to one of several types.
|
||||
*
|
||||
* Similar to boost::variant, but has no space overhead over a raw pointer, as
|
||||
* it relies on the fact that (on x86_64) there are 16 unused bits in a
|
||||
* pointer.
|
||||
*
|
||||
* @author Tudor Bosman (tudorb@fb.com)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <glog/logging.h>
|
||||
|
||||
#include <folly/Likely.h>
|
||||
#include <folly/Portability.h>
|
||||
#include <folly/detail/DiscriminatedPtrDetail.h>
|
||||
|
||||
#if !FOLLY_X64 && !FOLLY_AARCH64 && !FOLLY_PPC64
|
||||
#error "DiscriminatedPtr is x64, arm64 and ppc64 specific code."
|
||||
#endif
|
||||
|
||||
namespace folly {
|
||||
|
||||
/**
|
||||
* Discriminated pointer.
|
||||
*
|
||||
* Given a list of types, a DiscriminatedPtr<Types...> may point to an object
|
||||
* of one of the given types, or may be empty. DiscriminatedPtr is type-safe:
|
||||
* you may only get a pointer to the type that you put in, otherwise get
|
||||
* throws an exception (and get_nothrow returns nullptr)
|
||||
*
|
||||
* This pointer does not do any kind of lifetime management -- it's not a
|
||||
* "smart" pointer. You are responsible for deallocating any memory used
|
||||
* to hold pointees, if necessary.
|
||||
*/
|
||||
template <typename... Types>
|
||||
class DiscriminatedPtr {
|
||||
// <, not <=, as our indexes are 1-based (0 means "empty")
|
||||
static_assert(
|
||||
sizeof...(Types) < std::numeric_limits<uint16_t>::max(),
|
||||
"too many types");
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create an empty DiscriminatedPtr.
|
||||
*/
|
||||
DiscriminatedPtr() : data_(0) {}
|
||||
|
||||
/**
|
||||
* Create a DiscriminatedPtr that points to an object of type T.
|
||||
* Fails at compile time if T is not a valid type (listed in Types)
|
||||
*/
|
||||
template <typename T>
|
||||
explicit DiscriminatedPtr(T* ptr) {
|
||||
set(ptr, typeIndex<T>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this DiscriminatedPtr to point to an object of type T.
|
||||
* Fails at compile time if T is not a valid type (listed in Types)
|
||||
*/
|
||||
template <typename T>
|
||||
void set(T* ptr) {
|
||||
set(ptr, typeIndex<T>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a pointer to the object that this DiscriminatedPtr points to, if it is
|
||||
* of type T. Fails at compile time if T is not a valid type (listed in
|
||||
* Types), and returns nullptr if this DiscriminatedPtr is empty or points to
|
||||
* an object of a different type.
|
||||
*/
|
||||
template <typename T>
|
||||
T* get_nothrow() noexcept {
|
||||
void* p = LIKELY(hasType<T>()) ? ptr() : nullptr;
|
||||
return static_cast<T*>(p);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T* get_nothrow() const noexcept {
|
||||
const void* p = LIKELY(hasType<T>()) ? ptr() : nullptr;
|
||||
return static_cast<const T*>(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a pointer to the object that this DiscriminatedPtr points to, if it is
|
||||
* of type T. Fails at compile time if T is not a valid type (listed in
|
||||
* Types), and throws std::invalid_argument if this DiscriminatedPtr is empty
|
||||
* or points to an object of a different type.
|
||||
*/
|
||||
template <typename T>
|
||||
T* get() {
|
||||
if (UNLIKELY(!hasType<T>())) {
|
||||
throw std::invalid_argument("Invalid type");
|
||||
}
|
||||
return static_cast<T*>(ptr());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T* get() const {
|
||||
if (UNLIKELY(!hasType<T>())) {
|
||||
throw std::invalid_argument("Invalid type");
|
||||
}
|
||||
return static_cast<const T*>(ptr());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true iff this DiscriminatedPtr is empty.
|
||||
*/
|
||||
bool empty() const {
|
||||
return index() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true iff the object pointed by this DiscriminatedPtr has type T,
|
||||
* false otherwise. Fails at compile time if T is not a valid type (listed
|
||||
* in Types...)
|
||||
*/
|
||||
template <typename T>
|
||||
bool hasType() const {
|
||||
return index() == typeIndex<T>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear this DiscriminatedPtr, making it empty.
|
||||
*/
|
||||
void clear() {
|
||||
data_ = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assignment operator from a pointer of type T.
|
||||
*/
|
||||
template <typename T>
|
||||
DiscriminatedPtr& operator=(T* ptr) {
|
||||
set(ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a visitor to this object, calling the appropriate overload for
|
||||
* the type currently stored in DiscriminatedPtr. Throws invalid_argument
|
||||
* if the DiscriminatedPtr is empty.
|
||||
*
|
||||
* The visitor must meet the following requirements:
|
||||
*
|
||||
* - The visitor must allow invocation as a function by overloading
|
||||
* operator(), unambiguously accepting all values of type T* (or const T*)
|
||||
* for all T in Types...
|
||||
* - All operations of the function object on T* (or const T*) must
|
||||
* return the same type (or a static_assert will fire).
|
||||
*/
|
||||
template <typename V>
|
||||
typename dptr_detail::VisitorResult<V, Types...>::type apply(V&& visitor) {
|
||||
size_t n = index();
|
||||
if (n == 0) {
|
||||
throw std::invalid_argument("Empty DiscriminatedPtr");
|
||||
}
|
||||
return dptr_detail::ApplyVisitor<V, Types...>()(
|
||||
n, std::forward<V>(visitor), ptr());
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
typename dptr_detail::ConstVisitorResult<V, Types...>::type apply(
|
||||
V&& visitor) const {
|
||||
size_t n = index();
|
||||
if (n == 0) {
|
||||
throw std::invalid_argument("Empty DiscriminatedPtr");
|
||||
}
|
||||
return dptr_detail::ApplyConstVisitor<V, Types...>()(
|
||||
n, std::forward<V>(visitor), ptr());
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Get the 1-based type index of T in Types.
|
||||
*/
|
||||
template <typename T>
|
||||
uint16_t typeIndex() const {
|
||||
return uint16_t(dptr_detail::GetTypeIndex<T, Types...>::value);
|
||||
}
|
||||
|
||||
uint16_t index() const {
|
||||
return data_ >> 48;
|
||||
}
|
||||
void* ptr() const {
|
||||
return reinterpret_cast<void*>(data_ & ((1ULL << 48) - 1));
|
||||
}
|
||||
|
||||
void set(void* p, uint16_t v) {
|
||||
uintptr_t ip = reinterpret_cast<uintptr_t>(p);
|
||||
CHECK(!(ip >> 48));
|
||||
ip |= static_cast<uintptr_t>(v) << 48;
|
||||
data_ = ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* We store a pointer in the least significant 48 bits of data_, and a type
|
||||
* index (0 = empty, or 1-based index in Types) in the most significant 16
|
||||
* bits. We rely on the fact that pointers have their most significant 16
|
||||
* bits clear on x86_64.
|
||||
*/
|
||||
uintptr_t data_;
|
||||
};
|
||||
|
||||
template <typename Visitor, typename... Args>
|
||||
decltype(auto) apply_visitor(
|
||||
Visitor&& visitor,
|
||||
const DiscriminatedPtr<Args...>& variant) {
|
||||
return variant.apply(std::forward<Visitor>(visitor));
|
||||
}
|
||||
|
||||
template <typename Visitor, typename... Args>
|
||||
decltype(auto) apply_visitor(
|
||||
Visitor&& visitor,
|
||||
DiscriminatedPtr<Args...>& variant) {
|
||||
return variant.apply(std::forward<Visitor>(visitor));
|
||||
}
|
||||
|
||||
template <typename Visitor, typename... Args>
|
||||
decltype(auto) apply_visitor(
|
||||
Visitor&& visitor,
|
||||
DiscriminatedPtr<Args...>&& variant) {
|
||||
return variant.apply(std::forward<Visitor>(visitor));
|
||||
}
|
||||
|
||||
} // namespace folly
|
||||
@ -1,403 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// @author Nicholas Ormrod <njormrod@fb.com>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
#include <boost/iterator/iterator_adaptor.hpp>
|
||||
#include <boost/mpl/has_xxx.hpp>
|
||||
|
||||
#include <folly/Likely.h>
|
||||
#include <folly/Optional.h>
|
||||
#include <folly/Traits.h>
|
||||
#include <folly/dynamic.h>
|
||||
|
||||
namespace folly {
|
||||
template <typename T>
|
||||
T convertTo(const dynamic&);
|
||||
template <typename T>
|
||||
dynamic toDynamic(const T&);
|
||||
} // namespace folly
|
||||
|
||||
/**
|
||||
* convertTo returns a well-typed representation of the input dynamic.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* dynamic d = dynamic::array(
|
||||
* dynamic::array(1, 2, 3),
|
||||
* dynamic::array(4, 5)); // a vector of vector of int
|
||||
* auto vvi = convertTo<fbvector<fbvector<int>>>(d);
|
||||
*
|
||||
* See docs/DynamicConverter.md for supported types and customization
|
||||
*/
|
||||
|
||||
namespace folly {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// traits
|
||||
|
||||
namespace dynamicconverter_detail {
|
||||
|
||||
BOOST_MPL_HAS_XXX_TRAIT_DEF(value_type)
|
||||
BOOST_MPL_HAS_XXX_TRAIT_DEF(iterator)
|
||||
BOOST_MPL_HAS_XXX_TRAIT_DEF(mapped_type)
|
||||
BOOST_MPL_HAS_XXX_TRAIT_DEF(key_type)
|
||||
|
||||
template <typename T>
|
||||
struct iterator_class_is_container {
|
||||
typedef std::reverse_iterator<typename T::iterator> some_iterator;
|
||||
enum {
|
||||
value = has_value_type<T>::value &&
|
||||
std::is_constructible<T, some_iterator, some_iterator>::value
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using class_is_container =
|
||||
Conjunction<has_iterator<T>, iterator_class_is_container<T>>;
|
||||
|
||||
template <typename T>
|
||||
using is_range = StrictConjunction<has_value_type<T>, has_iterator<T>>;
|
||||
|
||||
template <typename T>
|
||||
using is_container = StrictConjunction<std::is_class<T>, class_is_container<T>>;
|
||||
|
||||
template <typename T>
|
||||
using is_map = StrictConjunction<is_range<T>, has_mapped_type<T>>;
|
||||
|
||||
template <typename T>
|
||||
using is_associative = StrictConjunction<is_range<T>, has_key_type<T>>;
|
||||
|
||||
} // namespace dynamicconverter_detail
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// custom iterators
|
||||
|
||||
/**
|
||||
* We have iterators that dereference to dynamics, but need iterators
|
||||
* that dereference to typename T.
|
||||
*
|
||||
* Implementation details:
|
||||
* 1. We cache the value of the dereference operator. This is necessary
|
||||
* because boost::iterator_adaptor requires *it to return a
|
||||
* reference.
|
||||
* 2. For const reasons, we cannot call operator= to refresh the
|
||||
* cache: we must call the destructor then placement new.
|
||||
*/
|
||||
|
||||
namespace dynamicconverter_detail {
|
||||
|
||||
template <typename T>
|
||||
struct Dereferencer {
|
||||
static inline void derefToCache(
|
||||
Optional<T>* /* mem */,
|
||||
const dynamic::const_item_iterator& /* it */) {
|
||||
throw TypeError("array", dynamic::Type::OBJECT);
|
||||
}
|
||||
|
||||
static inline void derefToCache(
|
||||
Optional<T>* mem,
|
||||
const dynamic::const_iterator& it) {
|
||||
mem->emplace(convertTo<T>(*it));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F, typename S>
|
||||
struct Dereferencer<std::pair<F, S>> {
|
||||
static inline void derefToCache(
|
||||
Optional<std::pair<F, S>>* mem,
|
||||
const dynamic::const_item_iterator& it) {
|
||||
mem->emplace(convertTo<F>(it->first), convertTo<S>(it->second));
|
||||
}
|
||||
|
||||
// Intentional duplication of the code in Dereferencer
|
||||
template <typename T>
|
||||
static inline void derefToCache(
|
||||
Optional<T>* mem,
|
||||
const dynamic::const_iterator& it) {
|
||||
mem->emplace(convertTo<T>(*it));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename It>
|
||||
class Transformer
|
||||
: public boost::
|
||||
iterator_adaptor<Transformer<T, It>, It, typename T::value_type> {
|
||||
friend class boost::iterator_core_access;
|
||||
|
||||
typedef typename T::value_type ttype;
|
||||
|
||||
mutable Optional<ttype> cache_;
|
||||
|
||||
void increment() {
|
||||
++this->base_reference();
|
||||
cache_ = none;
|
||||
}
|
||||
|
||||
ttype& dereference() const {
|
||||
if (!cache_) {
|
||||
Dereferencer<ttype>::derefToCache(&cache_, this->base_reference());
|
||||
}
|
||||
return cache_.value();
|
||||
}
|
||||
|
||||
public:
|
||||
explicit Transformer(const It& it) : Transformer::iterator_adaptor_(it) {}
|
||||
};
|
||||
|
||||
// conversion factory
|
||||
template <typename T, typename It>
|
||||
inline std::move_iterator<Transformer<T, It>> conversionIterator(const It& it) {
|
||||
return std::make_move_iterator(Transformer<T, It>(it));
|
||||
}
|
||||
|
||||
} // namespace dynamicconverter_detail
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// DynamicConverter specializations
|
||||
|
||||
/**
|
||||
* Each specialization of DynamicConverter has the function
|
||||
* 'static T convert(const dynamic&);'
|
||||
*/
|
||||
|
||||
// default - intentionally unimplemented
|
||||
template <typename T, typename Enable = void>
|
||||
struct DynamicConverter;
|
||||
|
||||
// boolean
|
||||
template <>
|
||||
struct DynamicConverter<bool> {
|
||||
static bool convert(const dynamic& d) {
|
||||
return d.asBool();
|
||||
}
|
||||
};
|
||||
|
||||
// integrals
|
||||
template <typename T>
|
||||
struct DynamicConverter<
|
||||
T,
|
||||
typename std::enable_if<
|
||||
std::is_integral<T>::value && !std::is_same<T, bool>::value>::type> {
|
||||
static T convert(const dynamic& d) {
|
||||
return folly::to<T>(d.asInt());
|
||||
}
|
||||
};
|
||||
|
||||
// enums
|
||||
template <typename T>
|
||||
struct DynamicConverter<
|
||||
T,
|
||||
typename std::enable_if<std::is_enum<T>::value>::type> {
|
||||
static T convert(const dynamic& d) {
|
||||
using type = typename std::underlying_type<T>::type;
|
||||
return static_cast<T>(DynamicConverter<type>::convert(d));
|
||||
}
|
||||
};
|
||||
|
||||
// floating point
|
||||
template <typename T>
|
||||
struct DynamicConverter<
|
||||
T,
|
||||
typename std::enable_if<std::is_floating_point<T>::value>::type> {
|
||||
static T convert(const dynamic& d) {
|
||||
return folly::to<T>(d.asDouble());
|
||||
}
|
||||
};
|
||||
|
||||
// fbstring
|
||||
template <>
|
||||
struct DynamicConverter<folly::fbstring> {
|
||||
static folly::fbstring convert(const dynamic& d) {
|
||||
return d.asString();
|
||||
}
|
||||
};
|
||||
|
||||
// std::string
|
||||
template <>
|
||||
struct DynamicConverter<std::string> {
|
||||
static std::string convert(const dynamic& d) {
|
||||
return d.asString();
|
||||
}
|
||||
};
|
||||
|
||||
// std::pair
|
||||
template <typename F, typename S>
|
||||
struct DynamicConverter<std::pair<F, S>> {
|
||||
static std::pair<F, S> convert(const dynamic& d) {
|
||||
if (d.isArray() && d.size() == 2) {
|
||||
return std::make_pair(convertTo<F>(d[0]), convertTo<S>(d[1]));
|
||||
} else if (d.isObject() && d.size() == 1) {
|
||||
auto it = d.items().begin();
|
||||
return std::make_pair(convertTo<F>(it->first), convertTo<S>(it->second));
|
||||
} else {
|
||||
throw TypeError("array (size 2) or object (size 1)", d.type());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// non-associative containers
|
||||
template <typename C>
|
||||
struct DynamicConverter<
|
||||
C,
|
||||
typename std::enable_if<
|
||||
dynamicconverter_detail::is_container<C>::value &&
|
||||
!dynamicconverter_detail::is_associative<C>::value>::type> {
|
||||
static C convert(const dynamic& d) {
|
||||
if (d.isArray()) {
|
||||
return C(
|
||||
dynamicconverter_detail::conversionIterator<C>(d.begin()),
|
||||
dynamicconverter_detail::conversionIterator<C>(d.end()));
|
||||
} else if (d.isObject()) {
|
||||
return C(
|
||||
dynamicconverter_detail::conversionIterator<C>(d.items().begin()),
|
||||
dynamicconverter_detail::conversionIterator<C>(d.items().end()));
|
||||
} else {
|
||||
throw TypeError("object or array", d.type());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// associative containers
|
||||
template <typename C>
|
||||
struct DynamicConverter<
|
||||
C,
|
||||
typename std::enable_if<
|
||||
dynamicconverter_detail::is_container<C>::value &&
|
||||
dynamicconverter_detail::is_associative<C>::value>::type> {
|
||||
static C convert(const dynamic& d) {
|
||||
C ret; // avoid direct initialization due to unordered_map's constructor
|
||||
// causing memory corruption if the iterator throws an exception
|
||||
if (d.isArray()) {
|
||||
ret.insert(
|
||||
dynamicconverter_detail::conversionIterator<C>(d.begin()),
|
||||
dynamicconverter_detail::conversionIterator<C>(d.end()));
|
||||
} else if (d.isObject()) {
|
||||
ret.insert(
|
||||
dynamicconverter_detail::conversionIterator<C>(d.items().begin()),
|
||||
dynamicconverter_detail::conversionIterator<C>(d.items().end()));
|
||||
} else {
|
||||
throw TypeError("object or array", d.type());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// DynamicConstructor specializations
|
||||
|
||||
/**
|
||||
* Each specialization of DynamicConstructor has the function
|
||||
* 'static dynamic construct(const C&);'
|
||||
*/
|
||||
|
||||
// default
|
||||
template <typename C, typename Enable = void>
|
||||
struct DynamicConstructor {
|
||||
static dynamic construct(const C& x) {
|
||||
return dynamic(x);
|
||||
}
|
||||
};
|
||||
|
||||
// identity
|
||||
template <typename C>
|
||||
struct DynamicConstructor<
|
||||
C,
|
||||
typename std::enable_if<std::is_same<C, dynamic>::value>::type> {
|
||||
static dynamic construct(const C& x) {
|
||||
return x;
|
||||
}
|
||||
};
|
||||
|
||||
// maps
|
||||
template <typename C>
|
||||
struct DynamicConstructor<
|
||||
C,
|
||||
typename std::enable_if<
|
||||
!std::is_same<C, dynamic>::value &&
|
||||
dynamicconverter_detail::is_map<C>::value>::type> {
|
||||
static dynamic construct(const C& x) {
|
||||
dynamic d = dynamic::object;
|
||||
for (const auto& pair : x) {
|
||||
d.insert(toDynamic(pair.first), toDynamic(pair.second));
|
||||
}
|
||||
return d;
|
||||
}
|
||||
};
|
||||
|
||||
// other ranges
|
||||
template <typename C>
|
||||
struct DynamicConstructor<
|
||||
C,
|
||||
typename std::enable_if<
|
||||
!std::is_same<C, dynamic>::value &&
|
||||
!dynamicconverter_detail::is_map<C>::value &&
|
||||
!std::is_constructible<StringPiece, const C&>::value &&
|
||||
dynamicconverter_detail::is_range<C>::value>::type> {
|
||||
static dynamic construct(const C& x) {
|
||||
dynamic d = dynamic::array;
|
||||
for (const auto& item : x) {
|
||||
d.push_back(toDynamic(item));
|
||||
}
|
||||
return d;
|
||||
}
|
||||
};
|
||||
|
||||
// pair
|
||||
template <typename A, typename B>
|
||||
struct DynamicConstructor<std::pair<A, B>, void> {
|
||||
static dynamic construct(const std::pair<A, B>& x) {
|
||||
dynamic d = dynamic::array;
|
||||
d.push_back(toDynamic(x.first));
|
||||
d.push_back(toDynamic(x.second));
|
||||
return d;
|
||||
}
|
||||
};
|
||||
|
||||
// vector<bool>
|
||||
template <>
|
||||
struct DynamicConstructor<std::vector<bool>, void> {
|
||||
static dynamic construct(const std::vector<bool>& x) {
|
||||
dynamic d = dynamic::array;
|
||||
// Intentionally specifying the type as bool here.
|
||||
// std::vector<bool>'s iterators return a proxy which is a prvalue
|
||||
// and hence cannot bind to an lvalue reference such as auto&
|
||||
for (bool item : x) {
|
||||
d.push_back(toDynamic(item));
|
||||
}
|
||||
return d;
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// implementation
|
||||
|
||||
template <typename T>
|
||||
T convertTo(const dynamic& d) {
|
||||
return DynamicConverter<typename std::remove_cv<T>::type>::convert(d);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
dynamic toDynamic(const T& x) {
|
||||
return DynamicConstructor<typename std::remove_cv<T>::type>::construct(x);
|
||||
}
|
||||
|
||||
} // namespace folly
|
||||
@ -1,142 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <stdexcept>
|
||||
#include <system_error>
|
||||
|
||||
#include <folly/Conv.h>
|
||||
#include <folly/FBString.h>
|
||||
#include <folly/Likely.h>
|
||||
#include <folly/Portability.h>
|
||||
|
||||
namespace folly {
|
||||
|
||||
// Various helpers to throw appropriate std::system_error exceptions from C
|
||||
// library errors (returned in errno, as positive return values (many POSIX
|
||||
// functions), or as negative return values (Linux syscalls))
|
||||
//
|
||||
// The *Explicit functions take an explicit value for errno.
|
||||
|
||||
inline std::system_error makeSystemErrorExplicit(int err, const char* msg) {
|
||||
// TODO: The C++ standard indicates that std::generic_category() should be
|
||||
// used for POSIX errno codes.
|
||||
//
|
||||
// We should ideally change this to use std::generic_category() instead of
|
||||
// std::system_category(). However, undertaking this change will require
|
||||
// updating existing call sites that currently catch exceptions thrown by
|
||||
// this code and currently expect std::system_category.
|
||||
return std::system_error(err, std::system_category(), msg);
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
std::system_error makeSystemErrorExplicit(int err, Args&&... args) {
|
||||
return makeSystemErrorExplicit(
|
||||
err, to<fbstring>(std::forward<Args>(args)...).c_str());
|
||||
}
|
||||
|
||||
inline std::system_error makeSystemError(const char* msg) {
|
||||
return makeSystemErrorExplicit(errno, msg);
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
std::system_error makeSystemError(Args&&... args) {
|
||||
return makeSystemErrorExplicit(errno, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// Helper to throw std::system_error
|
||||
[[noreturn]] inline void throwSystemErrorExplicit(int err, const char* msg) {
|
||||
throw makeSystemErrorExplicit(err, msg);
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
[[noreturn]] void throwSystemErrorExplicit(int err, Args&&... args) {
|
||||
throw makeSystemErrorExplicit(err, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// Helper to throw std::system_error from errno and components of a string
|
||||
template <class... Args>
|
||||
[[noreturn]] void throwSystemError(Args&&... args) {
|
||||
throwSystemErrorExplicit(errno, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// Check a Posix return code (0 on success, error number on error), throw
|
||||
// on error.
|
||||
template <class... Args>
|
||||
void checkPosixError(int err, Args&&... args) {
|
||||
if (UNLIKELY(err != 0)) {
|
||||
throwSystemErrorExplicit(err, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
// Check a Linux kernel-style return code (>= 0 on success, negative error
|
||||
// number on error), throw on error.
|
||||
template <class... Args>
|
||||
void checkKernelError(ssize_t ret, Args&&... args) {
|
||||
if (UNLIKELY(ret < 0)) {
|
||||
throwSystemErrorExplicit(int(-ret), std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
// Check a traditional Unix return code (-1 and sets errno on error), throw
|
||||
// on error.
|
||||
template <class... Args>
|
||||
void checkUnixError(ssize_t ret, Args&&... args) {
|
||||
if (UNLIKELY(ret == -1)) {
|
||||
throwSystemError(std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
void checkUnixErrorExplicit(ssize_t ret, int savedErrno, Args&&... args) {
|
||||
if (UNLIKELY(ret == -1)) {
|
||||
throwSystemErrorExplicit(savedErrno, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
// Check the return code from a fopen-style function (returns a non-nullptr
|
||||
// FILE* on success, nullptr on error, sets errno). Works with fopen, fdopen,
|
||||
// freopen, tmpfile, etc.
|
||||
template <class... Args>
|
||||
void checkFopenError(FILE* fp, Args&&... args) {
|
||||
if (UNLIKELY(!fp)) {
|
||||
throwSystemError(std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
void checkFopenErrorExplicit(FILE* fp, int savedErrno, Args&&... args) {
|
||||
if (UNLIKELY(!fp)) {
|
||||
throwSystemErrorExplicit(savedErrno, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If cond is not true, raise an exception of type E. E must have a ctor that
|
||||
* works with const char* (a description of the failure).
|
||||
*/
|
||||
#define CHECK_THROW(cond, E) \
|
||||
do { \
|
||||
if (!(cond)) { \
|
||||
throw E("Check failed: " #cond); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
} // namespace folly
|
||||
@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include <folly/Demangle.h>
|
||||
#include <folly/FBString.h>
|
||||
#include <folly/Portability.h>
|
||||
|
||||
namespace folly {
|
||||
|
||||
/**
|
||||
* Debug string for an exception: include type and what(), if
|
||||
* defined.
|
||||
*/
|
||||
inline fbstring exceptionStr(const std::exception& e) {
|
||||
#ifdef FOLLY_HAS_RTTI
|
||||
fbstring rv(demangle(typeid(e)));
|
||||
rv += ": ";
|
||||
#else
|
||||
fbstring rv("Exception (no RTTI available): ");
|
||||
#endif
|
||||
rv += e.what();
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Empirically, this indicates if the runtime supports
|
||||
// std::exception_ptr, as not all (arm, for instance) do.
|
||||
#if defined(__GNUC__) && defined(__GCC_ATOMIC_INT_LOCK_FREE) && \
|
||||
__GCC_ATOMIC_INT_LOCK_FREE > 1
|
||||
inline fbstring exceptionStr(std::exception_ptr ep) {
|
||||
try {
|
||||
std::rethrow_exception(ep);
|
||||
} catch (const std::exception& e) {
|
||||
return exceptionStr(e);
|
||||
} catch (...) {
|
||||
return "<unknown exception>";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename E>
|
||||
auto exceptionStr(const E& e) -> typename std::
|
||||
enable_if<!std::is_base_of<std::exception, E>::value, fbstring>::type {
|
||||
#ifdef FOLLY_HAS_RTTI
|
||||
return demangle(typeid(e));
|
||||
#else
|
||||
(void)e;
|
||||
return "Exception (no RTTI available) ";
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace folly
|
||||
@ -1,215 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <climits>
|
||||
|
||||
#include <folly/Function.h>
|
||||
#include <folly/Utility.h>
|
||||
|
||||
namespace folly {
|
||||
|
||||
using Func = Function<void()>;
|
||||
|
||||
/// An Executor accepts units of work with add(), which should be
|
||||
/// threadsafe.
|
||||
class Executor {
|
||||
public:
|
||||
// Workaround for a linkage problem with explicitly defaulted dtor t22914621
|
||||
virtual ~Executor() {}
|
||||
|
||||
/// Enqueue a function to executed by this executor. This and all
|
||||
/// variants must be threadsafe.
|
||||
virtual void add(Func) = 0;
|
||||
|
||||
/// Enqueue a function with a given priority, where 0 is the medium priority
|
||||
/// This is up to the implementation to enforce
|
||||
virtual void addWithPriority(Func, int8_t priority);
|
||||
|
||||
virtual uint8_t getNumPriorities() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const int8_t LO_PRI = SCHAR_MIN;
|
||||
static const int8_t MID_PRI = 0;
|
||||
static const int8_t HI_PRI = SCHAR_MAX;
|
||||
|
||||
template <typename ExecutorT = Executor>
|
||||
class KeepAlive {
|
||||
public:
|
||||
KeepAlive() = default;
|
||||
|
||||
~KeepAlive() {
|
||||
reset();
|
||||
}
|
||||
|
||||
KeepAlive(KeepAlive&& other) noexcept
|
||||
: executorAndDummyFlag_(exchange(other.executorAndDummyFlag_, 0)) {}
|
||||
|
||||
template <
|
||||
typename OtherExecutor,
|
||||
typename = typename std::enable_if<
|
||||
std::is_convertible<OtherExecutor*, ExecutorT*>::value>::type>
|
||||
/* implicit */ KeepAlive(KeepAlive<OtherExecutor>&& other) noexcept
|
||||
: KeepAlive(other.get(), other.executorAndDummyFlag_ & kDummyFlag) {
|
||||
other.executorAndDummyFlag_ = 0;
|
||||
}
|
||||
|
||||
KeepAlive& operator=(KeepAlive&& other) {
|
||||
reset();
|
||||
executorAndDummyFlag_ = exchange(other.executorAndDummyFlag_, 0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <
|
||||
typename OtherExecutor,
|
||||
typename = typename std::enable_if<
|
||||
std::is_convertible<OtherExecutor*, ExecutorT*>::value>::type>
|
||||
KeepAlive& operator=(KeepAlive<OtherExecutor>&& other) {
|
||||
return *this = KeepAlive(std::move(other));
|
||||
}
|
||||
|
||||
void reset() {
|
||||
if (Executor* executor = get()) {
|
||||
if (exchange(executorAndDummyFlag_, 0) & kDummyFlag) {
|
||||
return;
|
||||
}
|
||||
executor->keepAliveRelease();
|
||||
}
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return executorAndDummyFlag_;
|
||||
}
|
||||
|
||||
ExecutorT* get() const {
|
||||
return reinterpret_cast<ExecutorT*>(
|
||||
executorAndDummyFlag_ & kExecutorMask);
|
||||
}
|
||||
|
||||
ExecutorT& operator*() const {
|
||||
return *get();
|
||||
}
|
||||
|
||||
ExecutorT* operator->() const {
|
||||
return get();
|
||||
}
|
||||
|
||||
KeepAlive copy() const {
|
||||
return getKeepAliveToken(get());
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr intptr_t kDummyFlag = 1;
|
||||
static constexpr intptr_t kExecutorMask = ~kDummyFlag;
|
||||
|
||||
friend class Executor;
|
||||
template <typename OtherExecutor>
|
||||
friend class KeepAlive;
|
||||
|
||||
KeepAlive(ExecutorT* executor, bool dummy)
|
||||
: executorAndDummyFlag_(
|
||||
reinterpret_cast<intptr_t>(executor) | (dummy ? kDummyFlag : 0)) {
|
||||
assert(executor);
|
||||
assert(
|
||||
(reinterpret_cast<intptr_t>(executor) & kExecutorMask) ==
|
||||
reinterpret_cast<intptr_t>(executor));
|
||||
}
|
||||
|
||||
intptr_t executorAndDummyFlag_{reinterpret_cast<intptr_t>(nullptr)};
|
||||
};
|
||||
|
||||
template <typename ExecutorT>
|
||||
static KeepAlive<ExecutorT> getKeepAliveToken(ExecutorT* executor) {
|
||||
static_assert(
|
||||
std::is_base_of<Executor, ExecutorT>::value,
|
||||
"getKeepAliveToken only works for folly::Executor implementations.");
|
||||
if (!executor) {
|
||||
return {};
|
||||
}
|
||||
folly::Executor* executorPtr = executor;
|
||||
if (executorPtr->keepAliveAcquire()) {
|
||||
return makeKeepAlive<ExecutorT>(executor);
|
||||
}
|
||||
return makeKeepAliveDummy<ExecutorT>(executor);
|
||||
}
|
||||
|
||||
template <typename ExecutorT>
|
||||
static KeepAlive<ExecutorT> getKeepAliveToken(ExecutorT& executor) {
|
||||
static_assert(
|
||||
std::is_base_of<Executor, ExecutorT>::value,
|
||||
"getKeepAliveToken only works for folly::Executor implementations.");
|
||||
return getKeepAliveToken(&executor);
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Returns true if the KeepAlive is constructed from an executor that does
|
||||
* not support the keep alive ref-counting functionality
|
||||
*/
|
||||
template <typename ExecutorT>
|
||||
static bool isKeepAliveDummy(const KeepAlive<ExecutorT>& keepAlive) {
|
||||
return reinterpret_cast<intptr_t>(keepAlive.executorAndDummyFlag_) &
|
||||
KeepAlive<ExecutorT>::kDummyFlag;
|
||||
}
|
||||
|
||||
// Acquire a keep alive token. Should return false if keep-alive mechanism
|
||||
// is not supported.
|
||||
virtual bool keepAliveAcquire();
|
||||
// Release a keep alive token previously acquired by keepAliveAcquire().
|
||||
// Will never be called if keepAliveAcquire() returns false.
|
||||
virtual void keepAliveRelease();
|
||||
|
||||
template <typename ExecutorT>
|
||||
static KeepAlive<ExecutorT> makeKeepAlive(ExecutorT* executor) {
|
||||
static_assert(
|
||||
std::is_base_of<Executor, ExecutorT>::value,
|
||||
"makeKeepAlive only works for folly::Executor implementations.");
|
||||
return KeepAlive<ExecutorT>{executor, false};
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename ExecutorT>
|
||||
static KeepAlive<ExecutorT> makeKeepAliveDummy(ExecutorT* executor) {
|
||||
static_assert(
|
||||
std::is_base_of<Executor, ExecutorT>::value,
|
||||
"makeKeepAliveDummy only works for folly::Executor implementations.");
|
||||
return KeepAlive<ExecutorT>{executor, true};
|
||||
}
|
||||
};
|
||||
|
||||
/// Returns a keep-alive token which guarantees that Executor will keep
|
||||
/// processing tasks until the token is released (if supported by Executor).
|
||||
/// KeepAlive always contains a valid pointer to an Executor.
|
||||
template <typename ExecutorT>
|
||||
Executor::KeepAlive<ExecutorT> getKeepAliveToken(ExecutorT* executor) {
|
||||
static_assert(
|
||||
std::is_base_of<Executor, ExecutorT>::value,
|
||||
"getKeepAliveToken only works for folly::Executor implementations.");
|
||||
return Executor::getKeepAliveToken(executor);
|
||||
}
|
||||
|
||||
template <typename ExecutorT>
|
||||
Executor::KeepAlive<ExecutorT> getKeepAliveToken(ExecutorT& executor) {
|
||||
static_assert(
|
||||
std::is_base_of<Executor, ExecutorT>::value,
|
||||
"getKeepAliveToken only works for folly::Executor implementations.");
|
||||
return getKeepAliveToken(&executor);
|
||||
}
|
||||
|
||||
} // namespace folly
|
||||
@ -1,158 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
#include <folly/ExceptionWrapper.h>
|
||||
#include <folly/Expected.h>
|
||||
#include <folly/Portability.h>
|
||||
#include <folly/Range.h>
|
||||
#include <folly/portability/Unistd.h>
|
||||
|
||||
namespace folly {
|
||||
|
||||
/**
|
||||
* A File represents an open file.
|
||||
*/
|
||||
class File {
|
||||
public:
|
||||
/**
|
||||
* Creates an empty File object, for late initialization.
|
||||
*/
|
||||
File() noexcept;
|
||||
|
||||
/**
|
||||
* Create a File object from an existing file descriptor.
|
||||
* Takes ownership of the file descriptor if ownsFd is true.
|
||||
*/
|
||||
explicit File(int fd, bool ownsFd = false) noexcept;
|
||||
|
||||
/**
|
||||
* Open and create a file object. Throws on error.
|
||||
* Owns the file descriptor implicitly.
|
||||
*/
|
||||
explicit File(const char* name, int flags = O_RDONLY, mode_t mode = 0666);
|
||||
explicit File(
|
||||
const std::string& name,
|
||||
int flags = O_RDONLY,
|
||||
mode_t mode = 0666);
|
||||
explicit File(StringPiece name, int flags = O_RDONLY, mode_t mode = 0666);
|
||||
|
||||
/**
|
||||
* All the constructors that are not noexcept can throw std::system_error.
|
||||
* This is a helper method to use folly::Expected to chain a file open event
|
||||
* to something else you want to do with the open fd.
|
||||
*/
|
||||
template <typename... Args>
|
||||
static Expected<File, exception_wrapper> makeFile(Args&&... args) noexcept {
|
||||
try {
|
||||
return File(std::forward<Args>(args)...);
|
||||
} catch (const std::system_error& se) {
|
||||
return makeUnexpected(exception_wrapper(std::current_exception(), se));
|
||||
}
|
||||
}
|
||||
|
||||
~File();
|
||||
|
||||
/**
|
||||
* Create and return a temporary, owned file (uses tmpfile()).
|
||||
*/
|
||||
static File temporary();
|
||||
|
||||
/**
|
||||
* Return the file descriptor, or -1 if the file was closed.
|
||||
*/
|
||||
int fd() const {
|
||||
return fd_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' iff the file was successfully opened.
|
||||
*/
|
||||
explicit operator bool() const {
|
||||
return fd_ != -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate file descriptor and return File that owns it.
|
||||
*/
|
||||
File dup() const;
|
||||
|
||||
/**
|
||||
* If we own the file descriptor, close the file and throw on error.
|
||||
* Otherwise, do nothing.
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Closes the file (if owned). Returns true on success, false (and sets
|
||||
* errno) on error.
|
||||
*/
|
||||
bool closeNoThrow();
|
||||
|
||||
/**
|
||||
* Returns and releases the file descriptor; no longer owned by this File.
|
||||
* Returns -1 if the File object didn't wrap a file.
|
||||
*/
|
||||
int release() noexcept;
|
||||
|
||||
/**
|
||||
* Swap this File with another.
|
||||
*/
|
||||
void swap(File& other) noexcept;
|
||||
|
||||
// movable
|
||||
File(File&&) noexcept;
|
||||
File& operator=(File&&);
|
||||
|
||||
// FLOCK (INTERPROCESS) LOCKS
|
||||
//
|
||||
// NOTE THAT THESE LOCKS ARE flock() LOCKS. That is, they may only be used
|
||||
// for inter-process synchronization -- an attempt to acquire a second lock
|
||||
// on the same file descriptor from the same process may succeed. Attempting
|
||||
// to acquire a second lock on a different file descriptor for the same file
|
||||
// should fail, but some systems might implement flock() using fcntl() locks,
|
||||
// in which case it will succeed.
|
||||
void lock();
|
||||
bool try_lock();
|
||||
void unlock();
|
||||
|
||||
void lock_shared();
|
||||
bool try_lock_shared();
|
||||
void unlock_shared();
|
||||
|
||||
private:
|
||||
void doLock(int op);
|
||||
bool doTryLock(int op);
|
||||
|
||||
// unique
|
||||
File(const File&) = delete;
|
||||
File& operator=(const File&) = delete;
|
||||
|
||||
int fd_;
|
||||
bool ownsFd_;
|
||||
};
|
||||
|
||||
void swap(File& a, File& b) noexcept;
|
||||
|
||||
} // namespace folly
|
||||
@ -1,258 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
|
||||
#include <folly/Portability.h>
|
||||
#include <folly/Range.h>
|
||||
#include <folly/ScopeGuard.h>
|
||||
#include <folly/portability/Fcntl.h>
|
||||
#include <folly/portability/SysUio.h>
|
||||
#include <folly/portability/Unistd.h>
|
||||
|
||||
namespace folly {
|
||||
|
||||
/**
|
||||
* Convenience wrappers around some commonly used system calls. The *NoInt
|
||||
* wrappers retry on EINTR. The *Full wrappers retry on EINTR and also loop
|
||||
* until all data is written. Note that *Full wrappers weaken the thread
|
||||
* semantics of underlying system calls.
|
||||
*/
|
||||
int openNoInt(const char* name, int flags, mode_t mode = 0666);
|
||||
int closeNoInt(int fd);
|
||||
int dupNoInt(int fd);
|
||||
int dup2NoInt(int oldfd, int newfd);
|
||||
int fsyncNoInt(int fd);
|
||||
int fdatasyncNoInt(int fd);
|
||||
int ftruncateNoInt(int fd, off_t len);
|
||||
int truncateNoInt(const char* path, off_t len);
|
||||
int flockNoInt(int fd, int operation);
|
||||
int shutdownNoInt(int fd, int how);
|
||||
|
||||
ssize_t readNoInt(int fd, void* buf, size_t n);
|
||||
ssize_t preadNoInt(int fd, void* buf, size_t n, off_t offset);
|
||||
ssize_t readvNoInt(int fd, const iovec* iov, int count);
|
||||
|
||||
ssize_t writeNoInt(int fd, const void* buf, size_t n);
|
||||
ssize_t pwriteNoInt(int fd, const void* buf, size_t n, off_t offset);
|
||||
ssize_t writevNoInt(int fd, const iovec* iov, int count);
|
||||
|
||||
/**
|
||||
* Wrapper around read() (and pread()) that, in addition to retrying on
|
||||
* EINTR, will loop until all data is read.
|
||||
*
|
||||
* This wrapper is only useful for blocking file descriptors (for non-blocking
|
||||
* file descriptors, you have to be prepared to deal with incomplete reads
|
||||
* anyway), and only exists because POSIX allows read() to return an incomplete
|
||||
* read if interrupted by a signal (instead of returning -1 and setting errno
|
||||
* to EINTR).
|
||||
*
|
||||
* Note that this wrapper weakens the thread safety of read(): the file pointer
|
||||
* is shared between threads, but the system call is atomic. If multiple
|
||||
* threads are reading from a file at the same time, you don't know where your
|
||||
* data came from in the file, but you do know that the returned bytes were
|
||||
* contiguous. You can no longer make this assumption if using readFull().
|
||||
* You should probably use pread() when reading from the same file descriptor
|
||||
* from multiple threads simultaneously, anyway.
|
||||
*
|
||||
* Note that readvFull and preadvFull require iov to be non-const, unlike
|
||||
* readv and preadv. The contents of iov after these functions return
|
||||
* is unspecified.
|
||||
*/
|
||||
FOLLY_NODISCARD ssize_t readFull(int fd, void* buf, size_t n);
|
||||
FOLLY_NODISCARD ssize_t preadFull(int fd, void* buf, size_t n, off_t offset);
|
||||
FOLLY_NODISCARD ssize_t readvFull(int fd, iovec* iov, int count);
|
||||
FOLLY_NODISCARD ssize_t preadvFull(int fd, iovec* iov, int count, off_t offset);
|
||||
|
||||
/**
|
||||
* Similar to readFull and preadFull above, wrappers around write() and
|
||||
* pwrite() that loop until all data is written.
|
||||
*
|
||||
* Generally, the write() / pwrite() system call may always write fewer bytes
|
||||
* than requested, just like read(). In certain cases (such as when writing to
|
||||
* a pipe), POSIX provides stronger guarantees, but not in the general case.
|
||||
* For example, Linux (even on a 64-bit platform) won't write more than 2GB in
|
||||
* one write() system call.
|
||||
*
|
||||
* Note that writevFull and pwritevFull require iov to be non-const, unlike
|
||||
* writev and pwritev. The contents of iov after these functions return
|
||||
* is unspecified.
|
||||
*
|
||||
* These functions return -1 on error, or the total number of bytes written
|
||||
* (which is always the same as the number of requested bytes) on success.
|
||||
*/
|
||||
ssize_t writeFull(int fd, const void* buf, size_t n);
|
||||
ssize_t pwriteFull(int fd, const void* buf, size_t n, off_t offset);
|
||||
ssize_t writevFull(int fd, iovec* iov, int count);
|
||||
ssize_t pwritevFull(int fd, iovec* iov, int count, off_t offset);
|
||||
|
||||
/**
|
||||
* Read entire file (if num_bytes is defaulted) or no more than
|
||||
* num_bytes (otherwise) into container *out. The container is assumed
|
||||
* to be contiguous, with element size equal to 1, and offer size(),
|
||||
* reserve(), and random access (e.g. std::vector<char>, std::string,
|
||||
* fbstring).
|
||||
*
|
||||
* Returns: true on success or false on failure. In the latter case
|
||||
* errno will be set appropriately by the failing system primitive.
|
||||
*/
|
||||
template <class Container>
|
||||
bool readFile(
|
||||
int fd,
|
||||
Container& out,
|
||||
size_t num_bytes = std::numeric_limits<size_t>::max()) {
|
||||
static_assert(
|
||||
sizeof(out[0]) == 1,
|
||||
"readFile: only containers with byte-sized elements accepted");
|
||||
|
||||
size_t soFar = 0; // amount of bytes successfully read
|
||||
SCOPE_EXIT {
|
||||
DCHECK(out.size() >= soFar); // resize better doesn't throw
|
||||
out.resize(soFar);
|
||||
};
|
||||
|
||||
// Obtain file size:
|
||||
struct stat buf;
|
||||
if (fstat(fd, &buf) == -1) {
|
||||
return false;
|
||||
}
|
||||
// Some files (notably under /proc and /sys on Linux) lie about
|
||||
// their size, so treat the size advertised by fstat under advise
|
||||
// but don't rely on it. In particular, if the size is zero, we
|
||||
// should attempt to read stuff. If not zero, we'll attempt to read
|
||||
// one extra byte.
|
||||
constexpr size_t initialAlloc = 1024 * 4;
|
||||
out.resize(std::min(
|
||||
buf.st_size > 0 ? (size_t(buf.st_size) + 1) : initialAlloc, num_bytes));
|
||||
|
||||
while (soFar < out.size()) {
|
||||
const auto actual = readFull(fd, &out[soFar], out.size() - soFar);
|
||||
if (actual == -1) {
|
||||
return false;
|
||||
}
|
||||
soFar += actual;
|
||||
if (soFar < out.size()) {
|
||||
// File exhausted
|
||||
break;
|
||||
}
|
||||
// Ew, allocate more memory. Use exponential growth to avoid
|
||||
// quadratic behavior. Cap size to num_bytes.
|
||||
out.resize(std::min(out.size() * 3 / 2, num_bytes));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as above, but takes in a file name instead of fd
|
||||
*/
|
||||
template <class Container>
|
||||
bool readFile(
|
||||
const char* file_name,
|
||||
Container& out,
|
||||
size_t num_bytes = std::numeric_limits<size_t>::max()) {
|
||||
DCHECK(file_name);
|
||||
|
||||
const auto fd = openNoInt(file_name, O_RDONLY | O_CLOEXEC);
|
||||
if (fd == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SCOPE_EXIT {
|
||||
// Ignore errors when closing the file
|
||||
closeNoInt(fd);
|
||||
};
|
||||
|
||||
return readFile(fd, out, num_bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes container to file. The container is assumed to be
|
||||
* contiguous, with element size equal to 1, and offering STL-like
|
||||
* methods empty(), size(), and indexed access
|
||||
* (e.g. std::vector<char>, std::string, fbstring, StringPiece).
|
||||
*
|
||||
* "flags" dictates the open flags to use. Default is to create file
|
||||
* if it doesn't exist and truncate it.
|
||||
*
|
||||
* Returns: true on success or false on failure. In the latter case
|
||||
* errno will be set appropriately by the failing system primitive.
|
||||
*
|
||||
* Note that this function may leave the file in a partially written state on
|
||||
* failure. Use writeFileAtomic() if you want to ensure that the existing file
|
||||
* state will be unchanged on error.
|
||||
*/
|
||||
template <class Container>
|
||||
bool writeFile(
|
||||
const Container& data,
|
||||
const char* filename,
|
||||
int flags = O_WRONLY | O_CREAT | O_TRUNC,
|
||||
mode_t mode = 0666) {
|
||||
static_assert(
|
||||
sizeof(data[0]) == 1, "writeFile works with element size equal to 1");
|
||||
int fd = open(filename, flags, mode);
|
||||
if (fd == -1) {
|
||||
return false;
|
||||
}
|
||||
bool ok = data.empty() ||
|
||||
writeFull(fd, &data[0], data.size()) == static_cast<ssize_t>(data.size());
|
||||
return closeNoInt(fd) == 0 && ok;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write file contents "atomically".
|
||||
*
|
||||
* This writes the data to a temporary file in the destination directory, and
|
||||
* then renames it to the specified path. This guarantees that the specified
|
||||
* file will be replaced the the specified contents on success, or will not be
|
||||
* modified on failure.
|
||||
*
|
||||
* Note that on platforms that do not provide atomic filesystem rename
|
||||
* functionality (e.g., Windows) this behavior may not be truly atomic.
|
||||
*/
|
||||
void writeFileAtomic(
|
||||
StringPiece filename,
|
||||
iovec* iov,
|
||||
int count,
|
||||
mode_t permissions = 0644);
|
||||
void writeFileAtomic(
|
||||
StringPiece filename,
|
||||
ByteRange data,
|
||||
mode_t permissions = 0644);
|
||||
void writeFileAtomic(
|
||||
StringPiece filename,
|
||||
StringPiece data,
|
||||
mode_t permissions = 0644);
|
||||
|
||||
/**
|
||||
* A version of writeFileAtomic() that returns an errno value instead of
|
||||
* throwing on error.
|
||||
*
|
||||
* Returns 0 on success or an errno value on error.
|
||||
*/
|
||||
int writeFileAtomicNoThrow(
|
||||
StringPiece filename,
|
||||
iovec* iov,
|
||||
int count,
|
||||
mode_t permissions = 0644);
|
||||
|
||||
} // namespace folly
|
||||
@ -1,293 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Compute 64-, 96-, and 128-bit Rabin fingerprints, as described in
|
||||
* Michael O. Rabin (1981)
|
||||
* Fingerprinting by Random Polynomials
|
||||
* Center for Research in Computing Technology, Harvard University
|
||||
* Tech Report TR-CSE-03-01
|
||||
*
|
||||
* The implementation follows the optimization described in
|
||||
* Andrei Z. Broder (1993)
|
||||
* Some applications of Rabin's fingerprinting method
|
||||
*
|
||||
* extended for fingerprints larger than 64 bits, and modified to use
|
||||
* 64-bit instead of 32-bit integers for computation.
|
||||
*
|
||||
* The precomputed tables are in Fingerprint.cpp.
|
||||
*
|
||||
* Benchmarked on 10/13/2009 on a 2.5GHz quad-core Xeon L5420,
|
||||
* - Fingerprint<64>::update64() takes about 12ns
|
||||
* - Fingerprint<96>::update64() takes about 30ns
|
||||
* - Fingerprint<128>::update128() takes about 30ns
|
||||
* (unsurprisingly, Fingerprint<96> and Fingerprint<128> take the
|
||||
* same amount of time, as they both use 128-bit operations; the least
|
||||
* significant 32 bits of Fingerprint<96> will always be 0)
|
||||
*
|
||||
* @author Tudor Bosman (tudorb@facebook.com)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
#include <folly/Range.h>
|
||||
|
||||
namespace folly {
|
||||
|
||||
namespace detail {
|
||||
|
||||
constexpr size_t poly_size(size_t bits) {
|
||||
return 1 + (bits - 1) / 64;
|
||||
}
|
||||
|
||||
template <size_t Deg>
|
||||
using poly_table =
|
||||
std::array<std::array<std::array<uint64_t, poly_size(Deg)>, 256>, 8>;
|
||||
|
||||
template <int BITS>
|
||||
struct FingerprintTable {
|
||||
static const uint64_t poly[poly_size(BITS)];
|
||||
static const poly_table<BITS> table;
|
||||
};
|
||||
|
||||
template <int BITS>
|
||||
const uint64_t FingerprintTable<BITS>::poly[poly_size(BITS)] = {};
|
||||
template <int BITS>
|
||||
const poly_table<BITS> FingerprintTable<BITS>::table = {};
|
||||
|
||||
#ifndef _MSC_VER
|
||||
// MSVC 2015 can't handle these extern specialization declarations,
|
||||
// but they aren't needed for things to work right, so we just don't
|
||||
// declare them in the header for MSVC.
|
||||
|
||||
#define FOLLY_DECLARE_FINGERPRINT_TABLES(BITS) \
|
||||
template <> \
|
||||
const uint64_t FingerprintTable<BITS>::poly[poly_size(BITS)]; \
|
||||
template <> \
|
||||
const poly_table<BITS> FingerprintTable<BITS>::table
|
||||
|
||||
FOLLY_DECLARE_FINGERPRINT_TABLES(64);
|
||||
FOLLY_DECLARE_FINGERPRINT_TABLES(96);
|
||||
FOLLY_DECLARE_FINGERPRINT_TABLES(128);
|
||||
|
||||
#undef FOLLY_DECLARE_FINGERPRINT_TABLES
|
||||
#endif
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* Compute the Rabin fingerprint.
|
||||
*
|
||||
* TODO(tudorb): Extend this to allow removing values from the computed
|
||||
* fingerprint (so we can fingerprint a sliding window, as in the Rabin-Karp
|
||||
* string matching algorithm)
|
||||
*
|
||||
* update* methods return *this, so you can chain them together:
|
||||
* Fingerprint<96>().update8(x).update(str).update64(val).write(output);
|
||||
*/
|
||||
template <int BITS>
|
||||
class Fingerprint {
|
||||
public:
|
||||
Fingerprint() {
|
||||
// Use a non-zero starting value. We'll use (1 << (BITS-1))
|
||||
fp_[0] = 1ULL << 63;
|
||||
for (int i = 1; i < size(); i++) {
|
||||
fp_[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Fingerprint& update8(uint8_t v) {
|
||||
uint8_t out = shlor8(v);
|
||||
xortab(detail::FingerprintTable<BITS>::table[0][out]);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// update32 and update64 are convenience functions to update the fingerprint
|
||||
// with 4 and 8 bytes at a time. They are faster than calling update8
|
||||
// in a loop. They process the bytes in big-endian order.
|
||||
Fingerprint& update32(uint32_t v) {
|
||||
uint32_t out = shlor32(v);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
xortab(detail::FingerprintTable<BITS>::table[i][out & 0xff]);
|
||||
out >>= 8;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Fingerprint& update64(uint64_t v) {
|
||||
uint64_t out = shlor64(v);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
xortab(detail::FingerprintTable<BITS>::table[i][out & 0xff]);
|
||||
out >>= 8;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Fingerprint& update(StringPiece str) {
|
||||
// TODO(tudorb): We could be smart and do update64 or update32 if aligned
|
||||
for (auto c : str) {
|
||||
update8(uint8_t(c));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of uint64s needed to hold the fingerprint value.
|
||||
*/
|
||||
constexpr static int size() {
|
||||
return detail::poly_size(BITS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the computed fingeprint to an array of size() uint64_t's.
|
||||
* For Fingerprint<64>, size()==1; we write 64 bits in out[0]
|
||||
* For Fingerprint<96>, size()==2; we write 64 bits in out[0] and
|
||||
* the most significant 32 bits of out[1]
|
||||
* For Fingerprint<128>, size()==2; we write 64 bits in out[0] and
|
||||
* 64 bits in out[1].
|
||||
*/
|
||||
void write(uint64_t* out) const {
|
||||
for (int i = 0; i < size(); i++) {
|
||||
out[i] = fp_[i];
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// XOR the fingerprint with a value from one of the tables.
|
||||
void xortab(std::array<uint64_t, detail::poly_size(BITS)> const& tab) {
|
||||
for (int i = 0; i < size(); i++) {
|
||||
fp_[i] ^= tab[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions: shift the fingerprint value left by 8/32/64 bits,
|
||||
// return the "out" value (the bits that were shifted out), and add "v"
|
||||
// in the bits on the right.
|
||||
uint8_t shlor8(uint8_t v);
|
||||
uint32_t shlor32(uint32_t v);
|
||||
uint64_t shlor64(uint64_t v);
|
||||
|
||||
uint64_t fp_[detail::poly_size(BITS)];
|
||||
};
|
||||
|
||||
// Convenience functions
|
||||
|
||||
/**
|
||||
* Return the 64-bit Rabin fingerprint of a string.
|
||||
*/
|
||||
inline uint64_t fingerprint64(StringPiece str) {
|
||||
uint64_t fp;
|
||||
Fingerprint<64>().update(str).write(&fp);
|
||||
return fp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the 96-bit Rabin fingerprint of a string.
|
||||
* Return the 64 most significant bits in *msb, and the 32 least significant
|
||||
* bits in *lsb.
|
||||
*/
|
||||
inline void fingerprint96(StringPiece str, uint64_t* msb, uint32_t* lsb) {
|
||||
uint64_t fp[2];
|
||||
Fingerprint<96>().update(str).write(fp);
|
||||
*msb = fp[0];
|
||||
*lsb = (uint32_t)(fp[1] >> 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the 128-bit Rabin fingerprint of a string.
|
||||
* Return the 64 most significant bits in *msb, and the 64 least significant
|
||||
* bits in *lsb.
|
||||
*/
|
||||
inline void fingerprint128(StringPiece str, uint64_t* msb, uint64_t* lsb) {
|
||||
uint64_t fp[2];
|
||||
Fingerprint<128>().update(str).write(fp);
|
||||
*msb = fp[0];
|
||||
*lsb = fp[1];
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint8_t Fingerprint<64>::shlor8(uint8_t v) {
|
||||
uint8_t out = (uint8_t)(fp_[0] >> 56);
|
||||
fp_[0] = (fp_[0] << 8) | ((uint64_t)v);
|
||||
return out;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint32_t Fingerprint<64>::shlor32(uint32_t v) {
|
||||
uint32_t out = (uint32_t)(fp_[0] >> 32);
|
||||
fp_[0] = (fp_[0] << 32) | ((uint64_t)v);
|
||||
return out;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint64_t Fingerprint<64>::shlor64(uint64_t v) {
|
||||
uint64_t out = fp_[0];
|
||||
fp_[0] = v;
|
||||
return out;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint8_t Fingerprint<96>::shlor8(uint8_t v) {
|
||||
uint8_t out = (uint8_t)(fp_[0] >> 56);
|
||||
fp_[0] = (fp_[0] << 8) | (fp_[1] >> 56);
|
||||
fp_[1] = (fp_[1] << 8) | ((uint64_t)v << 32);
|
||||
return out;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint32_t Fingerprint<96>::shlor32(uint32_t v) {
|
||||
uint32_t out = (uint32_t)(fp_[0] >> 32);
|
||||
fp_[0] = (fp_[0] << 32) | (fp_[1] >> 32);
|
||||
fp_[1] = ((uint64_t)v << 32);
|
||||
return out;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint64_t Fingerprint<96>::shlor64(uint64_t v) {
|
||||
uint64_t out = fp_[0];
|
||||
fp_[0] = fp_[1] | (v >> 32);
|
||||
fp_[1] = v << 32;
|
||||
return out;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint8_t Fingerprint<128>::shlor8(uint8_t v) {
|
||||
uint8_t out = (uint8_t)(fp_[0] >> 56);
|
||||
fp_[0] = (fp_[0] << 8) | (fp_[1] >> 56);
|
||||
fp_[1] = (fp_[1] << 8) | ((uint64_t)v);
|
||||
return out;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint32_t Fingerprint<128>::shlor32(uint32_t v) {
|
||||
uint32_t out = (uint32_t)(fp_[0] >> 32);
|
||||
fp_[0] = (fp_[0] << 32) | (fp_[1] >> 32);
|
||||
fp_[1] = (fp_[1] << 32) | ((uint64_t)v);
|
||||
return out;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint64_t Fingerprint<128>::shlor64(uint64_t v) {
|
||||
uint64_t out = fp_[0];
|
||||
fp_[0] = fp_[1];
|
||||
fp_[1] = v;
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace folly
|
||||
@ -1,427 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <folly/Format.h>
|
||||
|
||||
#include <folly/ConstexprMath.h>
|
||||
#include <folly/CppAttributes.h>
|
||||
#include <folly/container/Array.h>
|
||||
|
||||
#include <double-conversion/double-conversion.h>
|
||||
|
||||
namespace folly {
|
||||
namespace detail {
|
||||
|
||||
// ctor for items in the align table
|
||||
struct format_table_align_make_item {
|
||||
static constexpr std::size_t size = 256;
|
||||
constexpr FormatArg::Align operator()(std::size_t index) const {
|
||||
// clang-format off
|
||||
return
|
||||
index == '<' ? FormatArg::Align::LEFT:
|
||||
index == '>' ? FormatArg::Align::RIGHT :
|
||||
index == '=' ? FormatArg::Align::PAD_AFTER_SIGN :
|
||||
index == '^' ? FormatArg::Align::CENTER :
|
||||
FormatArg::Align::INVALID;
|
||||
// clang-format on
|
||||
}
|
||||
};
|
||||
|
||||
// ctor for items in the conv tables for representing parts of nonnegative
|
||||
// integers into ascii digits of length Size, over a given base Base
|
||||
template <std::size_t Base, std::size_t Size, bool Upper = false>
|
||||
struct format_table_conv_make_item {
|
||||
static_assert(Base <= 36, "Base is unrepresentable");
|
||||
struct make_item {
|
||||
std::size_t index{};
|
||||
constexpr explicit make_item(std::size_t index_) : index(index_) {} // gcc49
|
||||
constexpr char alpha(std::size_t ord) const {
|
||||
return ord < 10 ? '0' + ord : (Upper ? 'A' : 'a') + (ord - 10);
|
||||
}
|
||||
constexpr char operator()(std::size_t offset) const {
|
||||
return alpha(index / constexpr_pow(Base, Size - offset - 1) % Base);
|
||||
}
|
||||
};
|
||||
constexpr std::array<char, Size> operator()(std::size_t index) const {
|
||||
return make_array_with<Size>(make_item{index});
|
||||
}
|
||||
};
|
||||
|
||||
// ctor for items in the sign table
|
||||
struct format_table_sign_make_item {
|
||||
static constexpr std::size_t size = 256;
|
||||
constexpr FormatArg::Sign operator()(std::size_t index) const {
|
||||
// clang-format off
|
||||
return
|
||||
index == '+' ? FormatArg::Sign::PLUS_OR_MINUS :
|
||||
index == '-' ? FormatArg::Sign::MINUS :
|
||||
index == ' ' ? FormatArg::Sign::SPACE_OR_MINUS :
|
||||
FormatArg::Sign::INVALID;
|
||||
// clang-format on
|
||||
}
|
||||
};
|
||||
|
||||
// the tables
|
||||
FOLLY_STORAGE_CONSTEXPR auto formatAlignTable =
|
||||
make_array_with<256>(format_table_align_make_item{});
|
||||
FOLLY_STORAGE_CONSTEXPR auto formatSignTable =
|
||||
make_array_with<256>(format_table_sign_make_item{});
|
||||
FOLLY_STORAGE_CONSTEXPR decltype(formatHexLower) formatHexLower =
|
||||
make_array_with<256>(format_table_conv_make_item<16, 2, false>{});
|
||||
FOLLY_STORAGE_CONSTEXPR decltype(formatHexUpper) formatHexUpper =
|
||||
make_array_with<256>(format_table_conv_make_item<16, 2, true>{});
|
||||
FOLLY_STORAGE_CONSTEXPR decltype(formatOctal) formatOctal =
|
||||
make_array_with<512>(format_table_conv_make_item<8, 3>{});
|
||||
FOLLY_STORAGE_CONSTEXPR decltype(formatBinary) formatBinary =
|
||||
make_array_with<256>(format_table_conv_make_item<2, 8>{});
|
||||
|
||||
} // namespace detail
|
||||
|
||||
using namespace folly::detail;
|
||||
|
||||
void FormatValue<double>::formatHelper(
|
||||
fbstring& piece,
|
||||
int& prefixLen,
|
||||
FormatArg& arg) const {
|
||||
using ::double_conversion::DoubleToStringConverter;
|
||||
using ::double_conversion::StringBuilder;
|
||||
|
||||
arg.validate(FormatArg::Type::FLOAT);
|
||||
|
||||
if (arg.presentation == FormatArg::kDefaultPresentation) {
|
||||
arg.presentation = 'g';
|
||||
}
|
||||
|
||||
const char* infinitySymbol = isupper(arg.presentation) ? "INF" : "inf";
|
||||
const char* nanSymbol = isupper(arg.presentation) ? "NAN" : "nan";
|
||||
char exponentSymbol = isupper(arg.presentation) ? 'E' : 'e';
|
||||
|
||||
if (arg.precision == FormatArg::kDefaultPrecision) {
|
||||
arg.precision = 6;
|
||||
}
|
||||
|
||||
// 2+: for null terminator and optional sign shenanigans.
|
||||
constexpr int bufLen = 2 +
|
||||
constexpr_max(2 + DoubleToStringConverter::kMaxFixedDigitsBeforePoint +
|
||||
DoubleToStringConverter::kMaxFixedDigitsAfterPoint,
|
||||
constexpr_max(
|
||||
8 + DoubleToStringConverter::kMaxExponentialDigits,
|
||||
7 + DoubleToStringConverter::kMaxPrecisionDigits));
|
||||
char buf[bufLen];
|
||||
StringBuilder builder(buf + 1, bufLen - 1);
|
||||
|
||||
char plusSign;
|
||||
switch (arg.sign) {
|
||||
case FormatArg::Sign::PLUS_OR_MINUS:
|
||||
plusSign = '+';
|
||||
break;
|
||||
case FormatArg::Sign::SPACE_OR_MINUS:
|
||||
plusSign = ' ';
|
||||
break;
|
||||
default:
|
||||
plusSign = '\0';
|
||||
break;
|
||||
};
|
||||
|
||||
auto flags = DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN |
|
||||
(arg.trailingDot ? DoubleToStringConverter::EMIT_TRAILING_DECIMAL_POINT
|
||||
: 0);
|
||||
|
||||
double val = val_;
|
||||
switch (arg.presentation) {
|
||||
case '%':
|
||||
val *= 100;
|
||||
FOLLY_FALLTHROUGH;
|
||||
case 'f':
|
||||
case 'F': {
|
||||
if (arg.precision > DoubleToStringConverter::kMaxFixedDigitsAfterPoint) {
|
||||
arg.precision = DoubleToStringConverter::kMaxFixedDigitsAfterPoint;
|
||||
}
|
||||
DoubleToStringConverter conv(
|
||||
flags,
|
||||
infinitySymbol,
|
||||
nanSymbol,
|
||||
exponentSymbol,
|
||||
-4,
|
||||
arg.precision,
|
||||
0,
|
||||
0);
|
||||
arg.enforce(
|
||||
conv.ToFixed(val, arg.precision, &builder),
|
||||
"fixed double conversion failed");
|
||||
break;
|
||||
}
|
||||
case 'e':
|
||||
case 'E': {
|
||||
if (arg.precision > DoubleToStringConverter::kMaxExponentialDigits) {
|
||||
arg.precision = DoubleToStringConverter::kMaxExponentialDigits;
|
||||
}
|
||||
|
||||
DoubleToStringConverter conv(
|
||||
flags,
|
||||
infinitySymbol,
|
||||
nanSymbol,
|
||||
exponentSymbol,
|
||||
-4,
|
||||
arg.precision,
|
||||
0,
|
||||
0);
|
||||
arg.enforce(conv.ToExponential(val, arg.precision, &builder));
|
||||
break;
|
||||
}
|
||||
case 'n': // should be locale-aware, but isn't
|
||||
case 'g':
|
||||
case 'G': {
|
||||
if (arg.precision < DoubleToStringConverter::kMinPrecisionDigits) {
|
||||
arg.precision = DoubleToStringConverter::kMinPrecisionDigits;
|
||||
} else if (arg.precision > DoubleToStringConverter::kMaxPrecisionDigits) {
|
||||
arg.precision = DoubleToStringConverter::kMaxPrecisionDigits;
|
||||
}
|
||||
DoubleToStringConverter conv(
|
||||
flags,
|
||||
infinitySymbol,
|
||||
nanSymbol,
|
||||
exponentSymbol,
|
||||
-4,
|
||||
arg.precision,
|
||||
0,
|
||||
0);
|
||||
arg.enforce(conv.ToShortest(val, &builder));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
arg.error("invalid specifier '", arg.presentation, "'");
|
||||
}
|
||||
|
||||
int len = builder.position();
|
||||
builder.Finalize();
|
||||
DCHECK_GT(len, 0);
|
||||
|
||||
// Add '+' or ' ' sign if needed
|
||||
char* p = buf + 1;
|
||||
// anything that's neither negative nor nan
|
||||
prefixLen = 0;
|
||||
if (plusSign && (*p != '-' && *p != 'n' && *p != 'N')) {
|
||||
*--p = plusSign;
|
||||
++len;
|
||||
prefixLen = 1;
|
||||
} else if (*p == '-') {
|
||||
prefixLen = 1;
|
||||
}
|
||||
|
||||
piece = fbstring(p, size_t(len));
|
||||
}
|
||||
|
||||
void FormatArg::initSlow() {
|
||||
auto b = fullArgString.begin();
|
||||
auto end = fullArgString.end();
|
||||
|
||||
// Parse key
|
||||
auto p = static_cast<const char*>(memchr(b, ':', size_t(end - b)));
|
||||
if (!p) {
|
||||
key_ = StringPiece(b, end);
|
||||
return;
|
||||
}
|
||||
key_ = StringPiece(b, p);
|
||||
|
||||
if (*p == ':') {
|
||||
// parse format spec
|
||||
if (++p == end) {
|
||||
return;
|
||||
}
|
||||
|
||||
// fill/align, or just align
|
||||
Align a;
|
||||
if (p + 1 != end &&
|
||||
(a = formatAlignTable[static_cast<unsigned char>(p[1])]) !=
|
||||
Align::INVALID) {
|
||||
fill = *p;
|
||||
align = a;
|
||||
p += 2;
|
||||
if (p == end) {
|
||||
return;
|
||||
}
|
||||
} else if (
|
||||
(a = formatAlignTable[static_cast<unsigned char>(*p)]) !=
|
||||
Align::INVALID) {
|
||||
align = a;
|
||||
if (++p == end) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Sign s;
|
||||
unsigned char uSign = static_cast<unsigned char>(*p);
|
||||
if ((s = formatSignTable[uSign]) != Sign::INVALID) {
|
||||
sign = s;
|
||||
if (++p == end) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (*p == '#') {
|
||||
basePrefix = true;
|
||||
if (++p == end) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (*p == '0') {
|
||||
enforce(align == Align::DEFAULT, "alignment specified twice");
|
||||
fill = '0';
|
||||
align = Align::PAD_AFTER_SIGN;
|
||||
if (++p == end) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto readInt = [&] {
|
||||
auto const c = p;
|
||||
do {
|
||||
++p;
|
||||
} while (p != end && *p >= '0' && *p <= '9');
|
||||
return to<int>(StringPiece(c, p));
|
||||
};
|
||||
|
||||
if (*p == '*') {
|
||||
width = kDynamicWidth;
|
||||
++p;
|
||||
|
||||
if (p == end) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (*p >= '0' && *p <= '9') {
|
||||
widthIndex = readInt();
|
||||
}
|
||||
|
||||
if (p == end) {
|
||||
return;
|
||||
}
|
||||
} else if (*p >= '0' && *p <= '9') {
|
||||
width = readInt();
|
||||
|
||||
if (p == end) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (*p == ',') {
|
||||
thousandsSeparator = true;
|
||||
if (++p == end) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (*p == '.') {
|
||||
auto d = ++p;
|
||||
while (p != end && *p >= '0' && *p <= '9') {
|
||||
++p;
|
||||
}
|
||||
if (p != d) {
|
||||
precision = to<int>(StringPiece(d, p));
|
||||
if (p != end && *p == '.') {
|
||||
trailingDot = true;
|
||||
++p;
|
||||
}
|
||||
} else {
|
||||
trailingDot = true;
|
||||
}
|
||||
|
||||
if (p == end) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
presentation = *p;
|
||||
if (++p == end) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
error("extra characters in format string");
|
||||
}
|
||||
|
||||
void FormatArg::validate(Type type) const {
|
||||
enforce(keyEmpty(), "index not allowed");
|
||||
switch (type) {
|
||||
case Type::INTEGER:
|
||||
enforce(
|
||||
precision == kDefaultPrecision, "precision not allowed on integers");
|
||||
break;
|
||||
case Type::FLOAT:
|
||||
enforce(
|
||||
!basePrefix, "base prefix ('#') specifier only allowed on integers");
|
||||
enforce(
|
||||
!thousandsSeparator,
|
||||
"thousands separator (',') only allowed on integers");
|
||||
break;
|
||||
case Type::OTHER:
|
||||
enforce(
|
||||
align != Align::PAD_AFTER_SIGN,
|
||||
"'='alignment only allowed on numbers");
|
||||
enforce(sign == Sign::DEFAULT, "sign specifier only allowed on numbers");
|
||||
enforce(
|
||||
!basePrefix, "base prefix ('#') specifier only allowed on integers");
|
||||
enforce(
|
||||
!thousandsSeparator,
|
||||
"thousands separator (',') only allowed on integers");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
void insertThousandsGroupingUnsafe(char* start_buffer, char** end_buffer) {
|
||||
uint32_t remaining_digits = uint32_t(*end_buffer - start_buffer);
|
||||
uint32_t separator_size = (remaining_digits - 1) / 3;
|
||||
uint32_t result_size = remaining_digits + separator_size;
|
||||
*end_buffer = *end_buffer + separator_size;
|
||||
|
||||
// get the end of the new string with the separators
|
||||
uint32_t buffer_write_index = result_size - 1;
|
||||
uint32_t buffer_read_index = remaining_digits - 1;
|
||||
start_buffer[buffer_write_index + 1] = 0;
|
||||
|
||||
bool done = false;
|
||||
uint32_t next_group_size = 3;
|
||||
|
||||
while (!done) {
|
||||
uint32_t current_group_size = std::max<uint32_t>(
|
||||
1, std::min<uint32_t>(remaining_digits, next_group_size));
|
||||
|
||||
// write out the current group's digits to the buffer index
|
||||
for (uint32_t i = 0; i < current_group_size; i++) {
|
||||
start_buffer[buffer_write_index--] = start_buffer[buffer_read_index--];
|
||||
}
|
||||
|
||||
// if not finished, write the separator before the next group
|
||||
if (buffer_write_index < buffer_write_index + 1) {
|
||||
start_buffer[buffer_write_index--] = ',';
|
||||
} else {
|
||||
done = true;
|
||||
}
|
||||
|
||||
remaining_digits -= current_group_size;
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
FormatKeyNotFoundException::FormatKeyNotFoundException(StringPiece key)
|
||||
: std::out_of_range(kMessagePrefix.str() + key.str()) {}
|
||||
|
||||
constexpr StringPiece const FormatKeyNotFoundException::kMessagePrefix;
|
||||
|
||||
} // namespace folly
|
||||
@ -1,497 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#define FOLLY_FORMAT_H_
|
||||
|
||||
#include <cstdio>
|
||||
#include <stdexcept>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#include <folly/CPortability.h>
|
||||
#include <folly/Conv.h>
|
||||
#include <folly/FormatArg.h>
|
||||
#include <folly/Range.h>
|
||||
#include <folly/String.h>
|
||||
#include <folly/Traits.h>
|
||||
|
||||
// Ignore shadowing warnings within this file, so includers can use -Wshadow.
|
||||
FOLLY_PUSH_WARNING
|
||||
FOLLY_GNU_DISABLE_WARNING("-Wshadow")
|
||||
|
||||
namespace folly {
|
||||
|
||||
// forward declarations
|
||||
template <bool containerMode, class... Args>
|
||||
class Formatter;
|
||||
template <class... Args>
|
||||
Formatter<false, Args...> format(StringPiece fmt, Args&&... args);
|
||||
template <class C>
|
||||
Formatter<true, C> vformat(StringPiece fmt, C&& container);
|
||||
template <class T, class Enable = void>
|
||||
class FormatValue;
|
||||
|
||||
// meta-attribute to identify formatters in this sea of template weirdness
|
||||
namespace detail {
|
||||
class FormatterTag {};
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* Formatter class.
|
||||
*
|
||||
* Note that this class is tricky, as it keeps *references* to its lvalue
|
||||
* arguments (while it takes ownership of the temporaries), and it doesn't
|
||||
* copy the passed-in format string. Thankfully, you can't use this
|
||||
* directly, you have to use format(...) below.
|
||||
*/
|
||||
|
||||
/* BaseFormatter class.
|
||||
* Overridable behaviours:
|
||||
* You may override the actual formatting of positional parameters in
|
||||
* `doFormatArg`. The Formatter class provides the default implementation.
|
||||
*
|
||||
* You may also override `doFormat` and `getSizeArg`. These override points were
|
||||
* added to permit static analysis of format strings, when it is inconvenient
|
||||
* or impossible to instantiate a BaseFormatter with the correct storage
|
||||
*/
|
||||
template <class Derived, bool containerMode, class... Args>
|
||||
class BaseFormatter {
|
||||
public:
|
||||
/**
|
||||
* Append to output. out(StringPiece sp) may be called (more than once)
|
||||
*/
|
||||
template <class Output>
|
||||
void operator()(Output& out) const;
|
||||
|
||||
/**
|
||||
* Append to a string.
|
||||
*/
|
||||
template <class Str>
|
||||
typename std::enable_if<IsSomeString<Str>::value>::type appendTo(
|
||||
Str& str) const {
|
||||
auto appender = [&str](StringPiece s) { str.append(s.data(), s.size()); };
|
||||
(*this)(appender);
|
||||
}
|
||||
|
||||
/**
|
||||
* Conversion to string
|
||||
*/
|
||||
std::string str() const {
|
||||
std::string s;
|
||||
appendTo(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Conversion to fbstring
|
||||
*/
|
||||
fbstring fbstr() const {
|
||||
fbstring s;
|
||||
appendTo(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Metadata to identify generated children of BaseFormatter
|
||||
*/
|
||||
typedef detail::FormatterTag IsFormatter;
|
||||
typedef BaseFormatter BaseType;
|
||||
|
||||
private:
|
||||
typedef std::tuple<Args...> ValueTuple;
|
||||
static constexpr size_t valueCount = std::tuple_size<ValueTuple>::value;
|
||||
|
||||
Derived const& asDerived() const {
|
||||
return *static_cast<const Derived*>(this);
|
||||
}
|
||||
|
||||
template <size_t K, class Callback>
|
||||
typename std::enable_if<K == valueCount>::type
|
||||
doFormatFrom(size_t i, FormatArg& arg, Callback& /*cb*/) const {
|
||||
arg.error("argument index out of range, max=", i);
|
||||
}
|
||||
|
||||
template <size_t K, class Callback>
|
||||
typename std::enable_if<(K < valueCount)>::type
|
||||
doFormatFrom(size_t i, FormatArg& arg, Callback& cb) const {
|
||||
if (i == K) {
|
||||
asDerived().template doFormatArg<K>(arg, cb);
|
||||
} else {
|
||||
doFormatFrom<K + 1>(i, arg, cb);
|
||||
}
|
||||
}
|
||||
|
||||
template <class Callback>
|
||||
void doFormat(size_t i, FormatArg& arg, Callback& cb) const {
|
||||
return doFormatFrom<0>(i, arg, cb);
|
||||
}
|
||||
|
||||
template <size_t K>
|
||||
typename std::enable_if<K == valueCount, int>::type getSizeArgFrom(
|
||||
size_t i,
|
||||
const FormatArg& arg) const {
|
||||
arg.error("argument index out of range, max=", i);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
typename std::enable_if<
|
||||
std::is_integral<T>::value && !std::is_same<T, bool>::value,
|
||||
int>::type
|
||||
getValue(const FormatValue<T>& format, const FormatArg&) const {
|
||||
return static_cast<int>(format.getValue());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
typename std::enable_if<
|
||||
!std::is_integral<T>::value || std::is_same<T, bool>::value,
|
||||
int>::type
|
||||
getValue(const FormatValue<T>&, const FormatArg& arg) const {
|
||||
arg.error("dynamic field width argument must be integral");
|
||||
}
|
||||
|
||||
template <size_t K>
|
||||
typename std::enable_if <
|
||||
K<valueCount, int>::type getSizeArgFrom(size_t i, const FormatArg& arg)
|
||||
const {
|
||||
if (i == K) {
|
||||
return getValue(getFormatValue<K>(), arg);
|
||||
}
|
||||
return getSizeArgFrom<K + 1>(i, arg);
|
||||
}
|
||||
|
||||
int getSizeArg(size_t i, const FormatArg& arg) const {
|
||||
return getSizeArgFrom<0>(i, arg);
|
||||
}
|
||||
|
||||
StringPiece str_;
|
||||
|
||||
protected:
|
||||
explicit BaseFormatter(StringPiece str, Args&&... args);
|
||||
|
||||
// Not copyable
|
||||
BaseFormatter(const BaseFormatter&) = delete;
|
||||
BaseFormatter& operator=(const BaseFormatter&) = delete;
|
||||
|
||||
// Movable, but the move constructor and assignment operator are private,
|
||||
// for the exclusive use of format() (below). This way, you can't create
|
||||
// a Formatter object, but can handle references to it (for streaming,
|
||||
// conversion to string, etc) -- which is good, as Formatter objects are
|
||||
// dangerous (they may hold references).
|
||||
BaseFormatter(BaseFormatter&&) = default;
|
||||
BaseFormatter& operator=(BaseFormatter&&) = default;
|
||||
|
||||
template <size_t K>
|
||||
using ArgType = typename std::tuple_element<K, ValueTuple>::type;
|
||||
|
||||
template <size_t K>
|
||||
FormatValue<typename std::decay<ArgType<K>>::type> getFormatValue() const {
|
||||
return FormatValue<typename std::decay<ArgType<K>>::type>(
|
||||
std::get<K>(values_));
|
||||
}
|
||||
|
||||
ValueTuple values_;
|
||||
};
|
||||
|
||||
template <bool containerMode, class... Args>
|
||||
class Formatter : public BaseFormatter<
|
||||
Formatter<containerMode, Args...>,
|
||||
containerMode,
|
||||
Args...> {
|
||||
private:
|
||||
explicit Formatter(StringPiece& str, Args&&... args)
|
||||
: BaseFormatter<
|
||||
Formatter<containerMode, Args...>,
|
||||
containerMode,
|
||||
Args...>(str, std::forward<Args>(args)...) {
|
||||
static_assert(
|
||||
!containerMode || sizeof...(Args) == 1,
|
||||
"Exactly one argument required in container mode");
|
||||
}
|
||||
|
||||
template <size_t K, class Callback>
|
||||
void doFormatArg(FormatArg& arg, Callback& cb) const {
|
||||
this->template getFormatValue<K>().format(arg, cb);
|
||||
}
|
||||
|
||||
friend class BaseFormatter<
|
||||
Formatter<containerMode, Args...>,
|
||||
containerMode,
|
||||
Args...>;
|
||||
|
||||
template <class... A>
|
||||
friend Formatter<false, A...> format(StringPiece fmt, A&&... arg);
|
||||
template <class C>
|
||||
friend Formatter<true, C> vformat(StringPiece fmt, C&& container);
|
||||
};
|
||||
|
||||
/**
|
||||
* Formatter objects can be written to streams.
|
||||
*/
|
||||
template <bool containerMode, class... Args>
|
||||
std::ostream& operator<<(
|
||||
std::ostream& out,
|
||||
const Formatter<containerMode, Args...>& formatter) {
|
||||
auto writer = [&out](StringPiece sp) {
|
||||
out.write(sp.data(), std::streamsize(sp.size()));
|
||||
};
|
||||
formatter(writer);
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formatter objects can be written to stdio FILEs.
|
||||
*/
|
||||
template <class Derived, bool containerMode, class... Args>
|
||||
void writeTo(
|
||||
FILE* fp,
|
||||
const BaseFormatter<Derived, containerMode, Args...>& formatter);
|
||||
|
||||
/**
|
||||
* Create a formatter object.
|
||||
*
|
||||
* std::string formatted = format("{} {}", 23, 42).str();
|
||||
* LOG(INFO) << format("{} {}", 23, 42);
|
||||
* writeTo(stdout, format("{} {}", 23, 42));
|
||||
*/
|
||||
template <class... Args>
|
||||
Formatter<false, Args...> format(StringPiece fmt, Args&&... args) {
|
||||
return Formatter<false, Args...>(fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like format(), but immediately returns the formatted string instead of an
|
||||
* intermediate format object.
|
||||
*/
|
||||
template <class... Args>
|
||||
inline std::string sformat(StringPiece fmt, Args&&... args) {
|
||||
return format(fmt, std::forward<Args>(args)...).str();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a formatter object that takes one argument (of container type)
|
||||
* and uses that container to get argument values from.
|
||||
*
|
||||
* std::map<string, string> map { {"hello", "world"}, {"answer", "42"} };
|
||||
*
|
||||
* The following are equivalent:
|
||||
* format("{0[hello]} {0[answer]}", map);
|
||||
*
|
||||
* vformat("{hello} {answer}", map);
|
||||
*
|
||||
* but the latter is cleaner.
|
||||
*/
|
||||
template <class Container>
|
||||
Formatter<true, Container> vformat(StringPiece fmt, Container&& container) {
|
||||
return Formatter<true, Container>(fmt, std::forward<Container>(container));
|
||||
}
|
||||
|
||||
/**
|
||||
* Like vformat(), but immediately returns the formatted string instead of an
|
||||
* intermediate format object.
|
||||
*/
|
||||
template <class Container>
|
||||
inline std::string svformat(StringPiece fmt, Container&& container) {
|
||||
return vformat(fmt, std::forward<Container>(container)).str();
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception class thrown when a format key is not found in the given
|
||||
* associative container keyed by strings. We inherit std::out_of_range for
|
||||
* compatibility with callers that expect exception to be thrown directly
|
||||
* by std::map or std::unordered_map.
|
||||
*
|
||||
* Having the key be at the end of the message string, we can access it by
|
||||
* simply adding its offset to what(). Not storing separate std::string key
|
||||
* makes the exception type small and noexcept-copyable like std::out_of_range,
|
||||
* and therefore able to fit in-situ in exception_wrapper.
|
||||
*/
|
||||
class FOLLY_EXPORT FormatKeyNotFoundException : public std::out_of_range {
|
||||
public:
|
||||
explicit FormatKeyNotFoundException(StringPiece key);
|
||||
|
||||
char const* key() const noexcept {
|
||||
return what() + kMessagePrefix.size();
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr StringPiece const kMessagePrefix = "format key not found: ";
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrap a sequence or associative container so that out-of-range lookups
|
||||
* return a default value rather than throwing an exception.
|
||||
*
|
||||
* Usage:
|
||||
* format("[no_such_key"], defaulted(map, 42)) -> 42
|
||||
*/
|
||||
namespace detail {
|
||||
template <class Container, class Value>
|
||||
struct DefaultValueWrapper {
|
||||
DefaultValueWrapper(const Container& container, const Value& defaultValue)
|
||||
: container(container), defaultValue(defaultValue) {}
|
||||
|
||||
const Container& container;
|
||||
const Value& defaultValue;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <class Container, class Value>
|
||||
detail::DefaultValueWrapper<Container, Value> defaulted(
|
||||
const Container& c,
|
||||
const Value& v) {
|
||||
return detail::DefaultValueWrapper<Container, Value>(c, v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append formatted output to a string.
|
||||
*
|
||||
* std::string foo;
|
||||
* format(&foo, "{} {}", 42, 23);
|
||||
*
|
||||
* Shortcut for toAppend(format(...), &foo);
|
||||
*/
|
||||
template <class Str, class... Args>
|
||||
typename std::enable_if<IsSomeString<Str>::value>::type
|
||||
format(Str* out, StringPiece fmt, Args&&... args) {
|
||||
format(fmt, std::forward<Args>(args)...).appendTo(*out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append vformatted output to a string.
|
||||
*/
|
||||
template <class Str, class Container>
|
||||
typename std::enable_if<IsSomeString<Str>::value>::type
|
||||
vformat(Str* out, StringPiece fmt, Container&& container) {
|
||||
vformat(fmt, std::forward<Container>(container)).appendTo(*out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utilities for all format value specializations.
|
||||
*/
|
||||
namespace format_value {
|
||||
|
||||
/**
|
||||
* Format a string in "val", obeying appropriate alignment, padding, width,
|
||||
* and precision. Treats Align::DEFAULT as Align::LEFT, and
|
||||
* Align::PAD_AFTER_SIGN as Align::RIGHT; use formatNumber for
|
||||
* number-specific formatting.
|
||||
*/
|
||||
template <class FormatCallback>
|
||||
void formatString(StringPiece val, FormatArg& arg, FormatCallback& cb);
|
||||
|
||||
/**
|
||||
* Format a number in "val"; the first prefixLen characters form the prefix
|
||||
* (sign, "0x" base prefix, etc) which must be left-aligned if the alignment
|
||||
* is Align::PAD_AFTER_SIGN. Treats Align::DEFAULT as Align::LEFT. Ignores
|
||||
* arg.precision, as that has a different meaning for numbers (not "maximum
|
||||
* field width")
|
||||
*/
|
||||
template <class FormatCallback>
|
||||
void formatNumber(
|
||||
StringPiece val,
|
||||
int prefixLen,
|
||||
FormatArg& arg,
|
||||
FormatCallback& cb);
|
||||
|
||||
/**
|
||||
* Format a Formatter object recursively. Behaves just like
|
||||
* formatString(fmt.str(), arg, cb); but avoids creating a temporary
|
||||
* string if possible.
|
||||
*/
|
||||
template <
|
||||
class FormatCallback,
|
||||
class Derived,
|
||||
bool containerMode,
|
||||
class... Args>
|
||||
void formatFormatter(
|
||||
const BaseFormatter<Derived, containerMode, Args...>& formatter,
|
||||
FormatArg& arg,
|
||||
FormatCallback& cb);
|
||||
|
||||
} // namespace format_value
|
||||
|
||||
/*
|
||||
* Specialize folly::FormatValue for your type.
|
||||
*
|
||||
* FormatValue<T> is constructed with a (reference-collapsed) T&&, which is
|
||||
* guaranteed to stay alive until the FormatValue object is destroyed, so you
|
||||
* may keep a reference (or pointer) to it instead of making a copy.
|
||||
*
|
||||
* You must define
|
||||
* template <class Callback>
|
||||
* void format(FormatArg& arg, Callback& cb) const;
|
||||
* with the following semantics: format the value using the given argument.
|
||||
*
|
||||
* arg is given by non-const reference for convenience -- it won't be reused,
|
||||
* so feel free to modify it in place if necessary. (For example, wrap an
|
||||
* existing conversion but change the default, or remove the "key" when
|
||||
* extracting an element from a container)
|
||||
*
|
||||
* Call the callback to append data to the output. You may call the callback
|
||||
* as many times as you'd like (or not at all, if you want to output an
|
||||
* empty string)
|
||||
*/
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <class T, class Enable = void>
|
||||
struct IsFormatter : public std::false_type {};
|
||||
|
||||
template <class T>
|
||||
struct IsFormatter<
|
||||
T,
|
||||
typename std::enable_if<
|
||||
std::is_same<typename T::IsFormatter, detail::FormatterTag>::value>::
|
||||
type> : public std::true_type {};
|
||||
} // namespace detail
|
||||
|
||||
// Deprecated API. formatChecked() et. al. now behave identically to their
|
||||
// non-Checked counterparts.
|
||||
template <class... Args>
|
||||
Formatter<false, Args...> formatChecked(StringPiece fmt, Args&&... args) {
|
||||
return format(fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
template <class... Args>
|
||||
inline std::string sformatChecked(StringPiece fmt, Args&&... args) {
|
||||
return formatChecked(fmt, std::forward<Args>(args)...).str();
|
||||
}
|
||||
template <class Container>
|
||||
Formatter<true, Container> vformatChecked(
|
||||
StringPiece fmt,
|
||||
Container&& container) {
|
||||
return vformat(fmt, std::forward<Container>(container));
|
||||
}
|
||||
template <class Container>
|
||||
inline std::string svformatChecked(StringPiece fmt, Container&& container) {
|
||||
return vformatChecked(fmt, std::forward<Container>(container)).str();
|
||||
}
|
||||
template <class Str, class... Args>
|
||||
typename std::enable_if<IsSomeString<Str>::value>::type
|
||||
formatChecked(Str* out, StringPiece fmt, Args&&... args) {
|
||||
formatChecked(fmt, std::forward<Args>(args)...).appendTo(*out);
|
||||
}
|
||||
template <class Str, class Container>
|
||||
typename std::enable_if<IsSomeString<Str>::value>::type
|
||||
vformatChecked(Str* out, StringPiece fmt, Container&& container) {
|
||||
vformatChecked(fmt, std::forward<Container>(container)).appendTo(*out);
|
||||
}
|
||||
|
||||
} // namespace folly
|
||||
|
||||
#include <folly/Format-inl.h>
|
||||
|
||||
FOLLY_POP_WARNING
|
||||
@ -1,279 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <folly/CPortability.h>
|
||||
#include <folly/Conv.h>
|
||||
#include <folly/Likely.h>
|
||||
#include <folly/Portability.h>
|
||||
#include <folly/Range.h>
|
||||
#include <folly/lang/Exception.h>
|
||||
|
||||
namespace folly {
|
||||
|
||||
class FOLLY_EXPORT BadFormatArg : public std::invalid_argument {
|
||||
using invalid_argument::invalid_argument;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parsed format argument.
|
||||
*/
|
||||
struct FormatArg {
|
||||
/**
|
||||
* Parse a format argument from a string. Keeps a reference to the
|
||||
* passed-in string -- does not copy the given characters.
|
||||
*/
|
||||
explicit FormatArg(StringPiece sp)
|
||||
: fullArgString(sp),
|
||||
fill(kDefaultFill),
|
||||
align(Align::DEFAULT),
|
||||
sign(Sign::DEFAULT),
|
||||
basePrefix(false),
|
||||
thousandsSeparator(false),
|
||||
trailingDot(false),
|
||||
width(kDefaultWidth),
|
||||
widthIndex(kNoIndex),
|
||||
precision(kDefaultPrecision),
|
||||
presentation(kDefaultPresentation),
|
||||
nextKeyMode_(NextKeyMode::NONE) {
|
||||
if (!sp.empty()) {
|
||||
initSlow();
|
||||
}
|
||||
}
|
||||
|
||||
enum class Type {
|
||||
INTEGER,
|
||||
FLOAT,
|
||||
OTHER,
|
||||
};
|
||||
/**
|
||||
* Validate the argument for the given type; throws on error.
|
||||
*/
|
||||
void validate(Type type) const;
|
||||
|
||||
/**
|
||||
* Throw an exception if the first argument is false. The exception
|
||||
* message will contain the argument string as well as any passed-in
|
||||
* arguments to enforce, formatted using folly::to<std::string>.
|
||||
*/
|
||||
template <typename... Args>
|
||||
void enforce(bool v, Args&&... args) const {
|
||||
if (UNLIKELY(!v)) {
|
||||
error(std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
std::string errorStr(Args&&... args) const;
|
||||
template <typename... Args>
|
||||
[[noreturn]] void error(Args&&... args) const;
|
||||
|
||||
/**
|
||||
* Full argument string, as passed in to the constructor.
|
||||
*/
|
||||
StringPiece fullArgString;
|
||||
|
||||
/**
|
||||
* Fill
|
||||
*/
|
||||
static constexpr char kDefaultFill = '\0';
|
||||
char fill;
|
||||
|
||||
/**
|
||||
* Alignment
|
||||
*/
|
||||
enum class Align : uint8_t {
|
||||
DEFAULT,
|
||||
LEFT,
|
||||
RIGHT,
|
||||
PAD_AFTER_SIGN,
|
||||
CENTER,
|
||||
INVALID,
|
||||
};
|
||||
Align align;
|
||||
|
||||
/**
|
||||
* Sign
|
||||
*/
|
||||
enum class Sign : uint8_t {
|
||||
DEFAULT,
|
||||
PLUS_OR_MINUS,
|
||||
MINUS,
|
||||
SPACE_OR_MINUS,
|
||||
INVALID,
|
||||
};
|
||||
Sign sign;
|
||||
|
||||
/**
|
||||
* Output base prefix (0 for octal, 0x for hex)
|
||||
*/
|
||||
bool basePrefix;
|
||||
|
||||
/**
|
||||
* Output thousands separator (comma)
|
||||
*/
|
||||
bool thousandsSeparator;
|
||||
|
||||
/**
|
||||
* Force a trailing decimal on doubles which could be rendered as ints
|
||||
*/
|
||||
bool trailingDot;
|
||||
|
||||
/**
|
||||
* Field width and optional argument index
|
||||
*/
|
||||
static constexpr int kDefaultWidth = -1;
|
||||
static constexpr int kDynamicWidth = -2;
|
||||
static constexpr int kNoIndex = -1;
|
||||
int width;
|
||||
int widthIndex;
|
||||
|
||||
/**
|
||||
* Precision
|
||||
*/
|
||||
static constexpr int kDefaultPrecision = -1;
|
||||
int precision;
|
||||
|
||||
/**
|
||||
* Presentation
|
||||
*/
|
||||
static constexpr char kDefaultPresentation = '\0';
|
||||
char presentation;
|
||||
|
||||
/**
|
||||
* Split a key component from "key", which must be non-empty (an exception
|
||||
* is thrown otherwise).
|
||||
*/
|
||||
template <bool emptyOk = false>
|
||||
StringPiece splitKey();
|
||||
|
||||
/**
|
||||
* Is the entire key empty?
|
||||
*/
|
||||
bool keyEmpty() const {
|
||||
return nextKeyMode_ == NextKeyMode::NONE && key_.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Split an key component from "key", which must be non-empty and a valid
|
||||
* integer (an exception is thrown otherwise).
|
||||
*/
|
||||
int splitIntKey();
|
||||
|
||||
void setNextIntKey(int val) {
|
||||
assert(nextKeyMode_ == NextKeyMode::NONE);
|
||||
nextKeyMode_ = NextKeyMode::INT;
|
||||
nextIntKey_ = val;
|
||||
}
|
||||
|
||||
void setNextKey(StringPiece val) {
|
||||
assert(nextKeyMode_ == NextKeyMode::NONE);
|
||||
nextKeyMode_ = NextKeyMode::STRING;
|
||||
nextKey_ = val;
|
||||
}
|
||||
|
||||
private:
|
||||
void initSlow();
|
||||
template <bool emptyOk>
|
||||
StringPiece doSplitKey();
|
||||
|
||||
StringPiece key_;
|
||||
int nextIntKey_;
|
||||
StringPiece nextKey_;
|
||||
enum class NextKeyMode {
|
||||
NONE,
|
||||
INT,
|
||||
STRING,
|
||||
};
|
||||
NextKeyMode nextKeyMode_;
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
inline std::string FormatArg::errorStr(Args&&... args) const {
|
||||
return to<std::string>(
|
||||
"invalid format argument {",
|
||||
fullArgString,
|
||||
"}: ",
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
[[noreturn]] inline void FormatArg::error(Args&&... args) const {
|
||||
throw_exception<BadFormatArg>(errorStr(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <bool emptyOk>
|
||||
inline StringPiece FormatArg::splitKey() {
|
||||
enforce(nextKeyMode_ != NextKeyMode::INT, "integer key expected");
|
||||
return doSplitKey<emptyOk>();
|
||||
}
|
||||
|
||||
template <bool emptyOk>
|
||||
inline StringPiece FormatArg::doSplitKey() {
|
||||
if (nextKeyMode_ == NextKeyMode::STRING) {
|
||||
nextKeyMode_ = NextKeyMode::NONE;
|
||||
if (!emptyOk) { // static
|
||||
enforce(!nextKey_.empty(), "non-empty key required");
|
||||
}
|
||||
return nextKey_;
|
||||
}
|
||||
|
||||
if (key_.empty()) {
|
||||
if (!emptyOk) { // static
|
||||
error("non-empty key required");
|
||||
}
|
||||
return StringPiece();
|
||||
}
|
||||
|
||||
const char* b = key_.begin();
|
||||
const char* e = key_.end();
|
||||
const char* p;
|
||||
if (e[-1] == ']') {
|
||||
--e;
|
||||
p = static_cast<const char*>(memchr(b, '[', size_t(e - b)));
|
||||
enforce(p != nullptr, "unmatched ']'");
|
||||
} else {
|
||||
p = static_cast<const char*>(memchr(b, '.', size_t(e - b)));
|
||||
}
|
||||
if (p) {
|
||||
key_.assign(p + 1, e);
|
||||
} else {
|
||||
p = e;
|
||||
key_.clear();
|
||||
}
|
||||
if (!emptyOk) { // static
|
||||
enforce(b != p, "non-empty key required");
|
||||
}
|
||||
return StringPiece(b, p);
|
||||
}
|
||||
|
||||
inline int FormatArg::splitIntKey() {
|
||||
if (nextKeyMode_ == NextKeyMode::INT) {
|
||||
nextKeyMode_ = NextKeyMode::NONE;
|
||||
return nextIntKey_;
|
||||
}
|
||||
try {
|
||||
return to<int>(doSplitKey<true>());
|
||||
} catch (const std::out_of_range&) {
|
||||
error("integer key required");
|
||||
return 0; // unreached
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace folly
|
||||
@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-present Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
|
||||
namespace folly {
|
||||
namespace detail {
|
||||
|
||||
// Shortcut, so we don't have to use enable_if everywhere
|
||||
struct FormatTraitsBase {
|
||||
typedef void enabled;
|
||||
};
|
||||
|
||||
// Traits that define enabled, value_type, and at() for anything
|
||||
// indexable with integral keys: pointers, arrays, vectors, and maps
|
||||
// with integral keys
|
||||
template <class T, class Enable = void>
|
||||
struct IndexableTraits;
|
||||
|
||||
// Base class for sequences (vectors, deques)
|
||||
template <class C>
|
||||
struct IndexableTraitsSeq : public FormatTraitsBase {
|
||||
typedef C container_type;
|
||||
typedef typename C::value_type value_type;
|
||||
|
||||
static const value_type& at(const C& c, int idx) {
|
||||
return c.at(idx);
|
||||
}
|
||||
|
||||
static const value_type& at(const C& c, int idx, const value_type& dflt) {
|
||||
return (idx >= 0 && size_t(idx) < c.size()) ? c.at(idx) : dflt;
|
||||
}
|
||||
};
|
||||
|
||||
// Base class for associative types (maps)
|
||||
template <class C>
|
||||
struct IndexableTraitsAssoc : public FormatTraitsBase {
|
||||
typedef typename C::value_type::second_type value_type;
|
||||
|
||||
static const value_type& at(const C& c, int idx) {
|
||||
return c.at(static_cast<typename C::key_type>(idx));
|
||||
}
|
||||
|
||||
static const value_type& at(const C& c, int idx, const value_type& dflt) {
|
||||
auto pos = c.find(static_cast<typename C::key_type>(idx));
|
||||
return pos != c.end() ? pos->second : dflt;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace folly
|
||||