Bug 1519636 - Ride along: Move to the unix CR type r=Ehsan

# ignore-this-changeset

Differential Revision: https://phabricator.services.mozilla.com/D38059

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Sylvestre Ledru 2019-07-16 07:34:18 +00:00
parent 2ff79fb0d5
commit ddf2513ea7
9 changed files with 1335 additions and 1335 deletions

View File

@ -1,26 +1,26 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "SkMemoryReporter.h"
#include "skia/include/core/SkGraphics.h"
namespace mozilla {
namespace gfx {
NS_IMETHODIMP
SkMemoryReporter::CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize) {
MOZ_COLLECT_REPORT("explicit/skia-font-cache", KIND_HEAP, UNITS_BYTES,
SkGraphics::GetFontCacheUsed(),
"Memory used in the skia font cache.");
return NS_OK;
}
NS_IMPL_ISUPPORTS(SkMemoryReporter, nsIMemoryReporter);
} // namespace gfx
} // namespace mozilla
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "SkMemoryReporter.h"
#include "skia/include/core/SkGraphics.h"
namespace mozilla {
namespace gfx {
NS_IMETHODIMP
SkMemoryReporter::CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize) {
MOZ_COLLECT_REPORT("explicit/skia-font-cache", KIND_HEAP, UNITS_BYTES,
SkGraphics::GetFontCacheUsed(),
"Memory used in the skia font cache.");
return NS_OK;
}
NS_IMPL_ISUPPORTS(SkMemoryReporter, nsIMemoryReporter);
} // namespace gfx
} // namespace mozilla

View File

@ -1,29 +1,29 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_SKMEMORYREPORTER_H
#define GFX_SKMEMORYREPORTER_H
#include "nsIMemoryReporter.h"
namespace mozilla {
namespace gfx {
class SkMemoryReporter final : public nsIMemoryReporter {
public:
NS_DECL_ISUPPORTS
NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize) override;
private:
~SkMemoryReporter() {}
};
} // namespace gfx
} // namespace mozilla
#endif /* GFX_SKMEMORYREPORTER_H */
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_SKMEMORYREPORTER_H
#define GFX_SKMEMORYREPORTER_H
#include "nsIMemoryReporter.h"
namespace mozilla {
namespace gfx {
class SkMemoryReporter final : public nsIMemoryReporter {
public:
NS_DECL_ISUPPORTS
NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize) override;
private:
~SkMemoryReporter() {}
};
} // namespace gfx
} // namespace mozilla
#endif /* GFX_SKMEMORYREPORTER_H */

View File

@ -1,66 +1,66 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_VR_VRSHMEM_H
#define GFX_VR_VRSHMEM_H
// This file declares VRShMem, which is used for cross-platform, cross-process
// communication between VR components.
// A shared memory handle is opened for the struct of type VRExternalShmem, and
// different processes write or read members of it to share data. Note that no
// process should be reading or writing to the same members, except for the
// versioning members used for synchronization.
#include "moz_external_vr.h"
#include "base/process.h" // for base::ProcessHandle
#include <functional>
namespace mozilla {
namespace gfx {
class VRShMem final {
public:
VRShMem(volatile VRExternalShmem* aShmem, bool aSameProcess,
bool aIsParentProcess);
~VRShMem() = default;
void CreateShMem();
void CloseShMem();
bool JoinShMem();
void LeaveShMem();
void PushBrowserState(VRBrowserState& aBrowserState, bool aNotifyCond);
void PullBrowserState(mozilla::gfx::VRBrowserState& aState);
void PushSystemState(const mozilla::gfx::VRSystemState& aState);
void PullSystemState(
VRDisplayState& aDisplayState, VRHMDSensorState& aSensorState,
VRControllerState (&aControllerState)[kVRControllerMaxCount],
bool& aEnumerationCompleted,
const std::function<bool()>& aWaitCondition = nullptr);
private:
volatile VRExternalShmem* mExternalShmem = nullptr;
bool mSameProcess = false;
#if defined(XP_WIN)
bool mIsParentProcess = false;
#endif
#if defined(XP_MACOSX)
int mShmemFD;
#elif defined(XP_WIN)
base::ProcessHandle mShmemFile;
base::ProcessHandle mMutex;
#endif
#if !defined(MOZ_WIDGET_ANDROID)
int64_t mBrowserGeneration = 0;
#endif
};
} // namespace gfx
} // namespace mozilla
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_VR_VRSHMEM_H
#define GFX_VR_VRSHMEM_H
// This file declares VRShMem, which is used for cross-platform, cross-process
// communication between VR components.
// A shared memory handle is opened for the struct of type VRExternalShmem, and
// different processes write or read members of it to share data. Note that no
// process should be reading or writing to the same members, except for the
// versioning members used for synchronization.
#include "moz_external_vr.h"
#include "base/process.h" // for base::ProcessHandle
#include <functional>
namespace mozilla {
namespace gfx {
class VRShMem final {
public:
VRShMem(volatile VRExternalShmem* aShmem, bool aSameProcess,
bool aIsParentProcess);
~VRShMem() = default;
void CreateShMem();
void CloseShMem();
bool JoinShMem();
void LeaveShMem();
void PushBrowserState(VRBrowserState& aBrowserState, bool aNotifyCond);
void PullBrowserState(mozilla::gfx::VRBrowserState& aState);
void PushSystemState(const mozilla::gfx::VRSystemState& aState);
void PullSystemState(
VRDisplayState& aDisplayState, VRHMDSensorState& aSensorState,
VRControllerState (&aControllerState)[kVRControllerMaxCount],
bool& aEnumerationCompleted,
const std::function<bool()>& aWaitCondition = nullptr);
private:
volatile VRExternalShmem* mExternalShmem = nullptr;
bool mSameProcess = false;
#if defined(XP_WIN)
bool mIsParentProcess = false;
#endif
#if defined(XP_MACOSX)
int mShmemFD;
#elif defined(XP_WIN)
base::ProcessHandle mShmemFile;
base::ProcessHandle mMutex;
#endif
#if !defined(MOZ_WIDGET_ANDROID)
int64_t mBrowserGeneration = 0;
#endif
};
} // namespace gfx
} // namespace mozilla
#endif

View File

@ -1,469 +1,469 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_VR_EXTERNAL_API_H
#define GFX_VR_EXTERNAL_API_H
#define GFX_VR_EIGHTCC(c1, c2, c3, c4, c5, c6, c7, c8) \
((uint64_t)(c1) << 56 | (uint64_t)(c2) << 48 | (uint64_t)(c3) << 40 | \
(uint64_t)(c4) << 32 | (uint64_t)(c5) << 24 | (uint64_t)(c6) << 16 | \
(uint64_t)(c7) << 8 | (uint64_t)(c8))
#ifdef MOZILLA_INTERNAL_API
# define __STDC_WANT_LIB_EXT1__ 1
// __STDC_WANT_LIB_EXT1__ required for memcpy_s
# include <stdlib.h>
# include <string.h>
# include "mozilla/TypedEnumBits.h"
# include "mozilla/gfx/2D.h"
# include <stddef.h>
# include <stdint.h>
# include <type_traits>
#endif // MOZILLA_INTERNAL_API
#if defined(__ANDROID__)
# include <pthread.h>
#endif // defined(__ANDROID__)
#include <cstdint>
#include <type_traits>
namespace mozilla {
#ifdef MOZILLA_INTERNAL_API
namespace dom {
enum class GamepadHand : uint8_t;
enum class GamepadCapabilityFlags : uint16_t;
} // namespace dom
#endif // MOZILLA_INTERNAL_API
namespace gfx {
static const int32_t kVRExternalVersion = 8;
// We assign VR presentations to groups with a bitmask.
// Currently, we will only display either content or chrome.
// Later, we will have more groups to support VR home spaces and
// multitasking environments.
// These values are not exposed to regular content and only affect
// chrome-only API's. They may be changed at any time.
static const uint32_t kVRGroupNone = 0;
static const uint32_t kVRGroupContent = 1 << 0;
static const uint32_t kVRGroupChrome = 1 << 1;
static const uint32_t kVRGroupAll = 0xffffffff;
static const int kVRDisplayNameMaxLen = 256;
static const int kVRControllerNameMaxLen = 256;
static const int kVRControllerMaxCount = 16;
static const int kVRControllerMaxButtons = 64;
static const int kVRControllerMaxAxis = 16;
static const int kVRLayerMaxCount = 8;
static const int kVRHapticsMaxCount = 32;
#if defined(__ANDROID__)
typedef uint64_t VRLayerTextureHandle;
#elif defined(XP_MACOSX)
typedef uint32_t VRLayerTextureHandle;
#else
typedef void* VRLayerTextureHandle;
#endif
struct Point3D_POD {
float x;
float y;
float z;
};
struct IntSize_POD {
int32_t width;
int32_t height;
};
struct FloatSize_POD {
float width;
float height;
};
#ifndef MOZILLA_INTERNAL_API
enum class ControllerHand : uint8_t { _empty, Left, Right, EndGuard_ };
enum class ControllerCapabilityFlags : uint16_t {
Cap_None = 0,
/**
* Cap_Position is set if the Gamepad is capable of tracking its position.
*/
Cap_Position = 1 << 1,
/**
* Cap_Orientation is set if the Gamepad is capable of tracking its
* orientation.
*/
Cap_Orientation = 1 << 2,
/**
* Cap_AngularAcceleration is set if the Gamepad is capable of tracking its
* angular acceleration.
*/
Cap_AngularAcceleration = 1 << 3,
/**
* Cap_LinearAcceleration is set if the Gamepad is capable of tracking its
* linear acceleration.
*/
Cap_LinearAcceleration = 1 << 4,
/**
* Cap_All used for validity checking during IPC serialization
*/
Cap_All = (1 << 5) - 1
};
#endif // ifndef MOZILLA_INTERNAL_API
enum class VRDisplayCapabilityFlags : uint16_t {
Cap_None = 0,
/**
* Cap_Position is set if the VRDisplay is capable of tracking its position.
*/
Cap_Position = 1 << 1,
/**
* Cap_Orientation is set if the VRDisplay is capable of tracking its
* orientation.
*/
Cap_Orientation = 1 << 2,
/**
* Cap_Present is set if the VRDisplay is capable of presenting content to an
* HMD or similar device. Can be used to indicate "magic window" devices that
* are capable of 6DoF tracking but for which requestPresent is not
* meaningful. If false then calls to requestPresent should always fail, and
* getEyeParameters should return null.
*/
Cap_Present = 1 << 3,
/**
* Cap_External is set if the VRDisplay is separate from the device's
* primary display. If presenting VR content will obscure
* other content on the device, this should be un-set. When
* un-set, the application should not attempt to mirror VR content
* or update non-VR UI because that content will not be visible.
*/
Cap_External = 1 << 4,
/**
* Cap_AngularAcceleration is set if the VRDisplay is capable of tracking its
* angular acceleration.
*/
Cap_AngularAcceleration = 1 << 5,
/**
* Cap_LinearAcceleration is set if the VRDisplay is capable of tracking its
* linear acceleration.
*/
Cap_LinearAcceleration = 1 << 6,
/**
* Cap_StageParameters is set if the VRDisplay is capable of room scale VR
* and can report the StageParameters to describe the space.
*/
Cap_StageParameters = 1 << 7,
/**
* Cap_MountDetection is set if the VRDisplay is capable of sensing when the
* user is wearing the device.
*/
Cap_MountDetection = 1 << 8,
/**
* Cap_PositionEmulated is set if the VRDisplay is capable of setting a
* emulated position (e.g. neck model) even if still doesn't support 6DOF
* tracking.
*/
Cap_PositionEmulated = 1 << 9,
/**
* Cap_All used for validity checking during IPC serialization
*/
Cap_All = (1 << 10) - 1
};
#ifdef MOZILLA_INTERNAL_API
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(VRDisplayCapabilityFlags)
#endif // MOZILLA_INTERNAL_API
struct VRPose {
float orientation[4];
float position[3];
float angularVelocity[3];
float angularAcceleration[3];
float linearVelocity[3];
float linearAcceleration[3];
};
struct VRHMDSensorState {
uint64_t inputFrameID;
double timestamp;
VRDisplayCapabilityFlags flags;
// These members will only change with inputFrameID:
VRPose pose;
float leftViewMatrix[16];
float rightViewMatrix[16];
#ifdef MOZILLA_INTERNAL_API
void Clear() { memset(this, 0, sizeof(VRHMDSensorState)); }
bool operator==(const VRHMDSensorState& other) const {
return inputFrameID == other.inputFrameID && timestamp == other.timestamp;
}
bool operator!=(const VRHMDSensorState& other) const {
return !(*this == other);
}
void CalcViewMatrices(const gfx::Matrix4x4* aHeadToEyeTransforms);
#endif // MOZILLA_INTERNAL_API
};
struct VRFieldOfView {
double upDegrees;
double rightDegrees;
double downDegrees;
double leftDegrees;
#ifdef MOZILLA_INTERNAL_API
VRFieldOfView() = default;
VRFieldOfView(double up, double right, double down, double left)
: upDegrees(up),
rightDegrees(right),
downDegrees(down),
leftDegrees(left) {}
void SetFromTanRadians(double up, double right, double down, double left) {
upDegrees = atan(up) * 180.0 / M_PI;
rightDegrees = atan(right) * 180.0 / M_PI;
downDegrees = atan(down) * 180.0 / M_PI;
leftDegrees = atan(left) * 180.0 / M_PI;
}
bool operator==(const VRFieldOfView& other) const {
return other.upDegrees == upDegrees && other.downDegrees == downDegrees &&
other.rightDegrees == rightDegrees &&
other.leftDegrees == leftDegrees;
}
bool operator!=(const VRFieldOfView& other) const {
return !(*this == other);
}
bool IsZero() const {
return upDegrees == 0.0 || rightDegrees == 0.0 || downDegrees == 0.0 ||
leftDegrees == 0.0;
}
Matrix4x4 ConstructProjectionMatrix(float zNear, float zFar,
bool rightHanded) const;
#endif // MOZILLA_INTERNAL_API
};
struct VRDisplayState {
enum Eye { Eye_Left, Eye_Right, NumEyes };
// When true, indicates that the VR service has shut down
bool shutdown;
// Minimum number of milliseconds to wait before attempting
// to start the VR service again
uint32_t minRestartInterval;
char displayName[kVRDisplayNameMaxLen];
// eight byte character code identifier
// LSB first, so "ABCDEFGH" -> ('H'<<56) + ('G'<<48) + ('F'<<40) +
// ('E'<<32) + ('D'<<24) + ('C'<<16) +
// ('B'<<8) + 'A').
uint64_t eightCC;
VRDisplayCapabilityFlags capabilityFlags;
VRFieldOfView eyeFOV[VRDisplayState::NumEyes];
Point3D_POD eyeTranslation[VRDisplayState::NumEyes];
IntSize_POD eyeResolution;
bool suppressFrames;
bool isConnected;
bool isMounted;
FloatSize_POD stageSize;
// We can't use a Matrix4x4 here unless we ensure it's a POD type
float sittingToStandingTransform[16];
uint64_t lastSubmittedFrameId;
bool lastSubmittedFrameSuccessful;
uint32_t presentingGeneration;
// Telemetry
bool reportsDroppedFrames;
uint64_t droppedFrameCount;
#ifdef MOZILLA_INTERNAL_API
void Clear() { memset(this, 0, sizeof(VRDisplayState)); }
#endif
};
struct VRControllerState {
char controllerName[kVRControllerNameMaxLen];
#ifdef MOZILLA_INTERNAL_API
dom::GamepadHand hand;
#else
ControllerHand hand;
#endif
uint32_t numButtons;
uint32_t numAxes;
uint32_t numHaptics;
// The current button pressed bit of button mask.
uint64_t buttonPressed;
// The current button touched bit of button mask.
uint64_t buttonTouched;
float triggerValue[kVRControllerMaxButtons];
float axisValue[kVRControllerMaxAxis];
#ifdef MOZILLA_INTERNAL_API
dom::GamepadCapabilityFlags flags;
#else
ControllerCapabilityFlags flags;
#endif
VRPose pose;
bool isPositionValid;
bool isOrientationValid;
#ifdef MOZILLA_INTERNAL_API
void Clear() { memset(this, 0, sizeof(VRControllerState)); }
#endif
};
struct VRLayerEyeRect {
float x;
float y;
float width;
float height;
};
enum class VRLayerType : uint16_t {
LayerType_None = 0,
LayerType_2D_Content = 1,
LayerType_Stereo_Immersive = 2
};
enum class VRLayerTextureType : uint16_t {
LayerTextureType_None = 0,
LayerTextureType_D3D10SurfaceDescriptor = 1,
LayerTextureType_MacIOSurface = 2,
LayerTextureType_GeckoSurfaceTexture = 3
};
struct VRLayer_2D_Content {
VRLayerTextureHandle textureHandle;
VRLayerTextureType textureType;
uint64_t frameId;
};
struct VRLayer_Stereo_Immersive {
VRLayerTextureHandle textureHandle;
VRLayerTextureType textureType;
uint64_t frameId;
uint64_t inputFrameId;
VRLayerEyeRect leftEyeRect;
VRLayerEyeRect rightEyeRect;
IntSize_POD textureSize;
};
struct VRLayerState {
VRLayerType type;
union {
VRLayer_2D_Content layer_2d_content;
VRLayer_Stereo_Immersive layer_stereo_immersive;
};
};
struct VRHapticState {
// Reference frame for timing.
// When 0, this does not represent an active haptic pulse.
uint64_t inputFrameID;
// Index within VRSystemState.controllerState identifying the controller
// to emit the haptic pulse
uint32_t controllerIndex;
// 0-based index indicating which haptic actuator within the controller
uint32_t hapticIndex;
// Start time of the haptic feedback pulse, relative to the start of
// inputFrameID, in seconds
float pulseStart;
// Duration of the haptic feedback pulse, in seconds
float pulseDuration;
// Intensity of the haptic feedback pulse, from 0.0f to 1.0f
float pulseIntensity;
};
struct VRBrowserState {
#if defined(__ANDROID__)
bool shutdown;
#endif // defined(__ANDROID__)
bool presentationActive;
bool navigationTransitionActive;
VRLayerState layerState[kVRLayerMaxCount];
VRHapticState hapticState[kVRHapticsMaxCount];
#ifdef MOZILLA_INTERNAL_API
void Clear() { memset(this, 0, sizeof(VRBrowserState)); }
#endif
};
struct VRSystemState {
bool enumerationCompleted;
VRDisplayState displayState;
VRHMDSensorState sensorState;
VRControllerState controllerState[kVRControllerMaxCount];
};
struct VRExternalShmem {
int32_t version;
int32_t size;
#if defined(__ANDROID__)
pthread_mutex_t systemMutex;
pthread_mutex_t geckoMutex;
pthread_mutex_t servoMutex;
pthread_cond_t systemCond;
pthread_cond_t geckoCond;
pthread_cond_t servoCond;
#else
int64_t generationA;
#endif // defined(__ANDROID__)
VRSystemState state;
#if !defined(__ANDROID__)
int64_t generationB;
int64_t geckoGenerationA;
int64_t servoGenerationA;
#endif // !defined(__ANDROID__)
VRBrowserState geckoState;
VRBrowserState servoState;
#if !defined(__ANDROID__)
int64_t geckoGenerationB;
int64_t servoGenerationB;
#endif // !defined(__ANDROID__)
#ifdef MOZILLA_INTERNAL_API
void Clear() volatile {
/**
* When possible we do a memset_s, which is explicitly safe for
* the volatile, POD struct. A memset may be optimized out by
* the compiler and will fail to compile due to volatile keyword
* propagation.
*
* A loop-based fallback is provided in case the toolchain does
* not include STDC_LIB_EXT1 for memset_s.
*/
# ifdef __STDC_LIB_EXT1__
memset_s((void*)this, sizeof(VRExternalShmem), 0, sizeof(VRExternalShmem));
# else
size_t remaining = sizeof(VRExternalShmem);
volatile unsigned char* d = (volatile unsigned char*)this;
while (remaining--) {
*d++ = 0;
}
# endif
}
#endif
};
// As we are memcpy'ing VRExternalShmem and its members around, it must be a POD
// type
static_assert(std::is_pod<VRExternalShmem>::value,
"VRExternalShmem must be a POD type.");
} // namespace gfx
} // namespace mozilla
#endif /* GFX_VR_EXTERNAL_API_H */
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_VR_EXTERNAL_API_H
#define GFX_VR_EXTERNAL_API_H
#define GFX_VR_EIGHTCC(c1, c2, c3, c4, c5, c6, c7, c8) \
((uint64_t)(c1) << 56 | (uint64_t)(c2) << 48 | (uint64_t)(c3) << 40 | \
(uint64_t)(c4) << 32 | (uint64_t)(c5) << 24 | (uint64_t)(c6) << 16 | \
(uint64_t)(c7) << 8 | (uint64_t)(c8))
#ifdef MOZILLA_INTERNAL_API
# define __STDC_WANT_LIB_EXT1__ 1
// __STDC_WANT_LIB_EXT1__ required for memcpy_s
# include <stdlib.h>
# include <string.h>
# include "mozilla/TypedEnumBits.h"
# include "mozilla/gfx/2D.h"
# include <stddef.h>
# include <stdint.h>
# include <type_traits>
#endif // MOZILLA_INTERNAL_API
#if defined(__ANDROID__)
# include <pthread.h>
#endif // defined(__ANDROID__)
#include <cstdint>
#include <type_traits>
namespace mozilla {
#ifdef MOZILLA_INTERNAL_API
namespace dom {
enum class GamepadHand : uint8_t;
enum class GamepadCapabilityFlags : uint16_t;
} // namespace dom
#endif // MOZILLA_INTERNAL_API
namespace gfx {
static const int32_t kVRExternalVersion = 8;
// We assign VR presentations to groups with a bitmask.
// Currently, we will only display either content or chrome.
// Later, we will have more groups to support VR home spaces and
// multitasking environments.
// These values are not exposed to regular content and only affect
// chrome-only API's. They may be changed at any time.
static const uint32_t kVRGroupNone = 0;
static const uint32_t kVRGroupContent = 1 << 0;
static const uint32_t kVRGroupChrome = 1 << 1;
static const uint32_t kVRGroupAll = 0xffffffff;
static const int kVRDisplayNameMaxLen = 256;
static const int kVRControllerNameMaxLen = 256;
static const int kVRControllerMaxCount = 16;
static const int kVRControllerMaxButtons = 64;
static const int kVRControllerMaxAxis = 16;
static const int kVRLayerMaxCount = 8;
static const int kVRHapticsMaxCount = 32;
#if defined(__ANDROID__)
typedef uint64_t VRLayerTextureHandle;
#elif defined(XP_MACOSX)
typedef uint32_t VRLayerTextureHandle;
#else
typedef void* VRLayerTextureHandle;
#endif
struct Point3D_POD {
float x;
float y;
float z;
};
struct IntSize_POD {
int32_t width;
int32_t height;
};
struct FloatSize_POD {
float width;
float height;
};
#ifndef MOZILLA_INTERNAL_API
enum class ControllerHand : uint8_t { _empty, Left, Right, EndGuard_ };
enum class ControllerCapabilityFlags : uint16_t {
Cap_None = 0,
/**
* Cap_Position is set if the Gamepad is capable of tracking its position.
*/
Cap_Position = 1 << 1,
/**
* Cap_Orientation is set if the Gamepad is capable of tracking its
* orientation.
*/
Cap_Orientation = 1 << 2,
/**
* Cap_AngularAcceleration is set if the Gamepad is capable of tracking its
* angular acceleration.
*/
Cap_AngularAcceleration = 1 << 3,
/**
* Cap_LinearAcceleration is set if the Gamepad is capable of tracking its
* linear acceleration.
*/
Cap_LinearAcceleration = 1 << 4,
/**
* Cap_All used for validity checking during IPC serialization
*/
Cap_All = (1 << 5) - 1
};
#endif // ifndef MOZILLA_INTERNAL_API
enum class VRDisplayCapabilityFlags : uint16_t {
Cap_None = 0,
/**
* Cap_Position is set if the VRDisplay is capable of tracking its position.
*/
Cap_Position = 1 << 1,
/**
* Cap_Orientation is set if the VRDisplay is capable of tracking its
* orientation.
*/
Cap_Orientation = 1 << 2,
/**
* Cap_Present is set if the VRDisplay is capable of presenting content to an
* HMD or similar device. Can be used to indicate "magic window" devices that
* are capable of 6DoF tracking but for which requestPresent is not
* meaningful. If false then calls to requestPresent should always fail, and
* getEyeParameters should return null.
*/
Cap_Present = 1 << 3,
/**
* Cap_External is set if the VRDisplay is separate from the device's
* primary display. If presenting VR content will obscure
* other content on the device, this should be un-set. When
* un-set, the application should not attempt to mirror VR content
* or update non-VR UI because that content will not be visible.
*/
Cap_External = 1 << 4,
/**
* Cap_AngularAcceleration is set if the VRDisplay is capable of tracking its
* angular acceleration.
*/
Cap_AngularAcceleration = 1 << 5,
/**
* Cap_LinearAcceleration is set if the VRDisplay is capable of tracking its
* linear acceleration.
*/
Cap_LinearAcceleration = 1 << 6,
/**
* Cap_StageParameters is set if the VRDisplay is capable of room scale VR
* and can report the StageParameters to describe the space.
*/
Cap_StageParameters = 1 << 7,
/**
* Cap_MountDetection is set if the VRDisplay is capable of sensing when the
* user is wearing the device.
*/
Cap_MountDetection = 1 << 8,
/**
* Cap_PositionEmulated is set if the VRDisplay is capable of setting a
* emulated position (e.g. neck model) even if still doesn't support 6DOF
* tracking.
*/
Cap_PositionEmulated = 1 << 9,
/**
* Cap_All used for validity checking during IPC serialization
*/
Cap_All = (1 << 10) - 1
};
#ifdef MOZILLA_INTERNAL_API
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(VRDisplayCapabilityFlags)
#endif // MOZILLA_INTERNAL_API
struct VRPose {
float orientation[4];
float position[3];
float angularVelocity[3];
float angularAcceleration[3];
float linearVelocity[3];
float linearAcceleration[3];
};
struct VRHMDSensorState {
uint64_t inputFrameID;
double timestamp;
VRDisplayCapabilityFlags flags;
// These members will only change with inputFrameID:
VRPose pose;
float leftViewMatrix[16];
float rightViewMatrix[16];
#ifdef MOZILLA_INTERNAL_API
void Clear() { memset(this, 0, sizeof(VRHMDSensorState)); }
bool operator==(const VRHMDSensorState& other) const {
return inputFrameID == other.inputFrameID && timestamp == other.timestamp;
}
bool operator!=(const VRHMDSensorState& other) const {
return !(*this == other);
}
void CalcViewMatrices(const gfx::Matrix4x4* aHeadToEyeTransforms);
#endif // MOZILLA_INTERNAL_API
};
struct VRFieldOfView {
double upDegrees;
double rightDegrees;
double downDegrees;
double leftDegrees;
#ifdef MOZILLA_INTERNAL_API
VRFieldOfView() = default;
VRFieldOfView(double up, double right, double down, double left)
: upDegrees(up),
rightDegrees(right),
downDegrees(down),
leftDegrees(left) {}
void SetFromTanRadians(double up, double right, double down, double left) {
upDegrees = atan(up) * 180.0 / M_PI;
rightDegrees = atan(right) * 180.0 / M_PI;
downDegrees = atan(down) * 180.0 / M_PI;
leftDegrees = atan(left) * 180.0 / M_PI;
}
bool operator==(const VRFieldOfView& other) const {
return other.upDegrees == upDegrees && other.downDegrees == downDegrees &&
other.rightDegrees == rightDegrees &&
other.leftDegrees == leftDegrees;
}
bool operator!=(const VRFieldOfView& other) const {
return !(*this == other);
}
bool IsZero() const {
return upDegrees == 0.0 || rightDegrees == 0.0 || downDegrees == 0.0 ||
leftDegrees == 0.0;
}
Matrix4x4 ConstructProjectionMatrix(float zNear, float zFar,
bool rightHanded) const;
#endif // MOZILLA_INTERNAL_API
};
struct VRDisplayState {
enum Eye { Eye_Left, Eye_Right, NumEyes };
// When true, indicates that the VR service has shut down
bool shutdown;
// Minimum number of milliseconds to wait before attempting
// to start the VR service again
uint32_t minRestartInterval;
char displayName[kVRDisplayNameMaxLen];
// eight byte character code identifier
// LSB first, so "ABCDEFGH" -> ('H'<<56) + ('G'<<48) + ('F'<<40) +
// ('E'<<32) + ('D'<<24) + ('C'<<16) +
// ('B'<<8) + 'A').
uint64_t eightCC;
VRDisplayCapabilityFlags capabilityFlags;
VRFieldOfView eyeFOV[VRDisplayState::NumEyes];
Point3D_POD eyeTranslation[VRDisplayState::NumEyes];
IntSize_POD eyeResolution;
bool suppressFrames;
bool isConnected;
bool isMounted;
FloatSize_POD stageSize;
// We can't use a Matrix4x4 here unless we ensure it's a POD type
float sittingToStandingTransform[16];
uint64_t lastSubmittedFrameId;
bool lastSubmittedFrameSuccessful;
uint32_t presentingGeneration;
// Telemetry
bool reportsDroppedFrames;
uint64_t droppedFrameCount;
#ifdef MOZILLA_INTERNAL_API
void Clear() { memset(this, 0, sizeof(VRDisplayState)); }
#endif
};
struct VRControllerState {
char controllerName[kVRControllerNameMaxLen];
#ifdef MOZILLA_INTERNAL_API
dom::GamepadHand hand;
#else
ControllerHand hand;
#endif
uint32_t numButtons;
uint32_t numAxes;
uint32_t numHaptics;
// The current button pressed bit of button mask.
uint64_t buttonPressed;
// The current button touched bit of button mask.
uint64_t buttonTouched;
float triggerValue[kVRControllerMaxButtons];
float axisValue[kVRControllerMaxAxis];
#ifdef MOZILLA_INTERNAL_API
dom::GamepadCapabilityFlags flags;
#else
ControllerCapabilityFlags flags;
#endif
VRPose pose;
bool isPositionValid;
bool isOrientationValid;
#ifdef MOZILLA_INTERNAL_API
void Clear() { memset(this, 0, sizeof(VRControllerState)); }
#endif
};
struct VRLayerEyeRect {
float x;
float y;
float width;
float height;
};
enum class VRLayerType : uint16_t {
LayerType_None = 0,
LayerType_2D_Content = 1,
LayerType_Stereo_Immersive = 2
};
enum class VRLayerTextureType : uint16_t {
LayerTextureType_None = 0,
LayerTextureType_D3D10SurfaceDescriptor = 1,
LayerTextureType_MacIOSurface = 2,
LayerTextureType_GeckoSurfaceTexture = 3
};
struct VRLayer_2D_Content {
VRLayerTextureHandle textureHandle;
VRLayerTextureType textureType;
uint64_t frameId;
};
struct VRLayer_Stereo_Immersive {
VRLayerTextureHandle textureHandle;
VRLayerTextureType textureType;
uint64_t frameId;
uint64_t inputFrameId;
VRLayerEyeRect leftEyeRect;
VRLayerEyeRect rightEyeRect;
IntSize_POD textureSize;
};
struct VRLayerState {
VRLayerType type;
union {
VRLayer_2D_Content layer_2d_content;
VRLayer_Stereo_Immersive layer_stereo_immersive;
};
};
struct VRHapticState {
// Reference frame for timing.
// When 0, this does not represent an active haptic pulse.
uint64_t inputFrameID;
// Index within VRSystemState.controllerState identifying the controller
// to emit the haptic pulse
uint32_t controllerIndex;
// 0-based index indicating which haptic actuator within the controller
uint32_t hapticIndex;
// Start time of the haptic feedback pulse, relative to the start of
// inputFrameID, in seconds
float pulseStart;
// Duration of the haptic feedback pulse, in seconds
float pulseDuration;
// Intensity of the haptic feedback pulse, from 0.0f to 1.0f
float pulseIntensity;
};
struct VRBrowserState {
#if defined(__ANDROID__)
bool shutdown;
#endif // defined(__ANDROID__)
bool presentationActive;
bool navigationTransitionActive;
VRLayerState layerState[kVRLayerMaxCount];
VRHapticState hapticState[kVRHapticsMaxCount];
#ifdef MOZILLA_INTERNAL_API
void Clear() { memset(this, 0, sizeof(VRBrowserState)); }
#endif
};
struct VRSystemState {
bool enumerationCompleted;
VRDisplayState displayState;
VRHMDSensorState sensorState;
VRControllerState controllerState[kVRControllerMaxCount];
};
struct VRExternalShmem {
int32_t version;
int32_t size;
#if defined(__ANDROID__)
pthread_mutex_t systemMutex;
pthread_mutex_t geckoMutex;
pthread_mutex_t servoMutex;
pthread_cond_t systemCond;
pthread_cond_t geckoCond;
pthread_cond_t servoCond;
#else
int64_t generationA;
#endif // defined(__ANDROID__)
VRSystemState state;
#if !defined(__ANDROID__)
int64_t generationB;
int64_t geckoGenerationA;
int64_t servoGenerationA;
#endif // !defined(__ANDROID__)
VRBrowserState geckoState;
VRBrowserState servoState;
#if !defined(__ANDROID__)
int64_t geckoGenerationB;
int64_t servoGenerationB;
#endif // !defined(__ANDROID__)
#ifdef MOZILLA_INTERNAL_API
void Clear() volatile {
/**
* When possible we do a memset_s, which is explicitly safe for
* the volatile, POD struct. A memset may be optimized out by
* the compiler and will fail to compile due to volatile keyword
* propagation.
*
* A loop-based fallback is provided in case the toolchain does
* not include STDC_LIB_EXT1 for memset_s.
*/
# ifdef __STDC_LIB_EXT1__
memset_s((void*)this, sizeof(VRExternalShmem), 0, sizeof(VRExternalShmem));
# else
size_t remaining = sizeof(VRExternalShmem);
volatile unsigned char* d = (volatile unsigned char*)this;
while (remaining--) {
*d++ = 0;
}
# endif
}
#endif
};
// As we are memcpy'ing VRExternalShmem and its members around, it must be a POD
// type
static_assert(std::is_pod<VRExternalShmem>::value,
"VRExternalShmem must be a POD type.");
} // namespace gfx
} // namespace mozilla
#endif /* GFX_VR_EXTERNAL_API_H */

View File

@ -1,33 +1,33 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <windows.h>
#include "vrhost/vrhostex.h"
int main(int argc, char* argv[], char* envp[]) {
HINSTANCE hVR = ::LoadLibrary("vrhost.dll");
if (hVR != nullptr) {
PFN_SAMPLE lpfnSample = (PFN_SAMPLE)GetProcAddress(hVR, "SampleExport");
if (lpfnSample != nullptr) {
lpfnSample();
}
if (strcmp(argv[1], "-testsvc") == 0) {
PFN_SAMPLE lpfnService =
(PFN_SAMPLE)GetProcAddress(hVR, "TestTheService");
lpfnService();
} else if (strcmp(argv[1], "-testmgr") == 0) {
PFN_SAMPLE lpfnManager =
(PFN_SAMPLE)GetProcAddress(hVR, "TestTheManager");
lpfnManager();
}
::FreeLibrary(hVR);
hVR = nullptr;
}
return 0;
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <windows.h>
#include "vrhost/vrhostex.h"
int main(int argc, char* argv[], char* envp[]) {
HINSTANCE hVR = ::LoadLibrary("vrhost.dll");
if (hVR != nullptr) {
PFN_SAMPLE lpfnSample = (PFN_SAMPLE)GetProcAddress(hVR, "SampleExport");
if (lpfnSample != nullptr) {
lpfnSample();
}
if (strcmp(argv[1], "-testsvc") == 0) {
PFN_SAMPLE lpfnService =
(PFN_SAMPLE)GetProcAddress(hVR, "TestTheService");
lpfnService();
} else if (strcmp(argv[1], "-testmgr") == 0) {
PFN_SAMPLE lpfnManager =
(PFN_SAMPLE)GetProcAddress(hVR, "TestTheManager");
lpfnManager();
}
::FreeLibrary(hVR);
hVR = nullptr;
}
return 0;
}

View File

@ -1,139 +1,139 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// vrhost.cpp
// Definition of functions that are exported from this dll
#include "vrhostex.h"
#include "VRShMem.h"
#include <stdio.h>
#include "windows.h"
static const char s_pszSharedEvent[] = "vrhost_test_event_signal";
static const DWORD s_dwWFSO_WAIT = 20000;
void SampleExport() { printf("vrhost.cpp hello world"); }
// For testing ShMem as Manager and Service:
// The two processes should output the steps, synchronously, to validate
// 2-way communication via VRShMem as follows
// 01 mgr: create mgr
// 02 mgr: wait for signal
// 03 svc: create svc
// 04 svc: send signal
// 05 svc: wait for signal
// 06 mgr: push browser
// 07 mgr: send signal
// 08 mgr: wait for signal
// 09 svc: pull browser
// 10 svc: verify data
// 11 svc: push system
// 12 svc: send signal
// 13 mgr: pull system
// 14 mgr: verify data
// 15 return
// These tests can be run with two instances of vrtesthost.exe, one first
// running with -testmgr and the second running with -testsvc.
// TODO: Bug 1563235 - Convert vrtesthost.exe tests into unit tests
// For testing VRShMem as the Manager (i.e., the one who creates the
// shmem). The sequence of how it tests with the service is listed above.
void TestTheManager() {
HANDLE hEvent = ::CreateEventA(nullptr, // lpEventAttributes
FALSE, // bManualReset
FALSE, // bInitialState
s_pszSharedEvent // lpName
);
printf("\n01 mgr: create mgr\n");
mozilla::gfx::VRShMem shmem(nullptr, false, false);
shmem.CreateShMem();
printf("02 mgr: wait for signal\n");
::WaitForSingleObject(hEvent, s_dwWFSO_WAIT);
// Set some state to verify on the other side
mozilla::gfx::VRBrowserState browserState = {0};
browserState.presentationActive = true;
browserState.layerState[0].type =
mozilla::gfx::VRLayerType::LayerType_2D_Content;
browserState.hapticState[0].controllerIndex = 987;
printf("06 mgr: push browser\n");
shmem.PushBrowserState(browserState, true);
printf("07 mgr: send signal\n");
::SetEvent(hEvent);
printf("08 mgr: wait for signal\n");
::WaitForSingleObject(hEvent, s_dwWFSO_WAIT);
printf("13 mgr: pull system\n");
mozilla::gfx::VRSystemState state;
shmem.PullSystemState(state.displayState, state.sensorState,
state.controllerState, state.enumerationCompleted,
nullptr);
printf(
"14 mgr: verify data\n"
"\tstate.enumerationCompleted = %d\n"
"\tstate.displayState.displayName = \"%s\"\n"
"\tstate.controllerState[1].hand = %hhu\n"
"\tstate.sensorState.inputFrameID = %llu\n",
state.enumerationCompleted, state.displayState.displayName,
state.controllerState[1].hand, state.sensorState.inputFrameID);
shmem.CloseShMem();
}
// For testing VRShMem as the Service (i.e., the one who consumes the
// shmem). The sequence of how it tests with the service is listed above.
void TestTheService() {
// Handle created by BeTheManager above.
HANDLE hEvent = ::OpenEventA(EVENT_ALL_ACCESS, // dwDesiredAccess
FALSE, // bInheritHandle
s_pszSharedEvent // lpName
);
printf("\n03 svc: create svc\n");
mozilla::gfx::VRShMem shmem(nullptr, false, false);
shmem.JoinShMem();
printf("04 svc: send signal\n");
::SetEvent(hEvent);
printf("05 svc: wait for signal\n");
::WaitForSingleObject(hEvent, s_dwWFSO_WAIT);
printf("09 svc: pull browser\n");
mozilla::gfx::VRBrowserState state;
shmem.PullBrowserState(state);
printf(
"10 svc: verify data\n"
"\tstate.presentationActive = %d\n"
"\tstate.layerState[0].type = %hu\n"
"\tstate.hapticState[0].controllerIndex = %d\n",
state.presentationActive, state.layerState[0].type,
state.hapticState[0].controllerIndex);
// Set some state to verify on the other side
mozilla::gfx::VRSystemState systemState;
systemState.enumerationCompleted = true;
strncpy(systemState.displayState.displayName, "test from vrservice shmem",
mozilla::gfx::kVRDisplayNameMaxLen);
systemState.controllerState[1].hand = mozilla::gfx::ControllerHand::Left;
systemState.sensorState.inputFrameID = 1234567;
printf("11 svc: push system\n");
shmem.PushSystemState(systemState);
printf("12 svc: send signal\n");
::SetEvent(hEvent);
shmem.LeaveShMem();
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// vrhost.cpp
// Definition of functions that are exported from this dll
#include "vrhostex.h"
#include "VRShMem.h"
#include <stdio.h>
#include "windows.h"
static const char s_pszSharedEvent[] = "vrhost_test_event_signal";
static const DWORD s_dwWFSO_WAIT = 20000;
void SampleExport() { printf("vrhost.cpp hello world"); }
// For testing ShMem as Manager and Service:
// The two processes should output the steps, synchronously, to validate
// 2-way communication via VRShMem as follows
// 01 mgr: create mgr
// 02 mgr: wait for signal
// 03 svc: create svc
// 04 svc: send signal
// 05 svc: wait for signal
// 06 mgr: push browser
// 07 mgr: send signal
// 08 mgr: wait for signal
// 09 svc: pull browser
// 10 svc: verify data
// 11 svc: push system
// 12 svc: send signal
// 13 mgr: pull system
// 14 mgr: verify data
// 15 return
// These tests can be run with two instances of vrtesthost.exe, one first
// running with -testmgr and the second running with -testsvc.
// TODO: Bug 1563235 - Convert vrtesthost.exe tests into unit tests
// For testing VRShMem as the Manager (i.e., the one who creates the
// shmem). The sequence of how it tests with the service is listed above.
void TestTheManager() {
HANDLE hEvent = ::CreateEventA(nullptr, // lpEventAttributes
FALSE, // bManualReset
FALSE, // bInitialState
s_pszSharedEvent // lpName
);
printf("\n01 mgr: create mgr\n");
mozilla::gfx::VRShMem shmem(nullptr, false, false);
shmem.CreateShMem();
printf("02 mgr: wait for signal\n");
::WaitForSingleObject(hEvent, s_dwWFSO_WAIT);
// Set some state to verify on the other side
mozilla::gfx::VRBrowserState browserState = {0};
browserState.presentationActive = true;
browserState.layerState[0].type =
mozilla::gfx::VRLayerType::LayerType_2D_Content;
browserState.hapticState[0].controllerIndex = 987;
printf("06 mgr: push browser\n");
shmem.PushBrowserState(browserState, true);
printf("07 mgr: send signal\n");
::SetEvent(hEvent);
printf("08 mgr: wait for signal\n");
::WaitForSingleObject(hEvent, s_dwWFSO_WAIT);
printf("13 mgr: pull system\n");
mozilla::gfx::VRSystemState state;
shmem.PullSystemState(state.displayState, state.sensorState,
state.controllerState, state.enumerationCompleted,
nullptr);
printf(
"14 mgr: verify data\n"
"\tstate.enumerationCompleted = %d\n"
"\tstate.displayState.displayName = \"%s\"\n"
"\tstate.controllerState[1].hand = %hhu\n"
"\tstate.sensorState.inputFrameID = %llu\n",
state.enumerationCompleted, state.displayState.displayName,
state.controllerState[1].hand, state.sensorState.inputFrameID);
shmem.CloseShMem();
}
// For testing VRShMem as the Service (i.e., the one who consumes the
// shmem). The sequence of how it tests with the service is listed above.
void TestTheService() {
// Handle created by BeTheManager above.
HANDLE hEvent = ::OpenEventA(EVENT_ALL_ACCESS, // dwDesiredAccess
FALSE, // bInheritHandle
s_pszSharedEvent // lpName
);
printf("\n03 svc: create svc\n");
mozilla::gfx::VRShMem shmem(nullptr, false, false);
shmem.JoinShMem();
printf("04 svc: send signal\n");
::SetEvent(hEvent);
printf("05 svc: wait for signal\n");
::WaitForSingleObject(hEvent, s_dwWFSO_WAIT);
printf("09 svc: pull browser\n");
mozilla::gfx::VRBrowserState state;
shmem.PullBrowserState(state);
printf(
"10 svc: verify data\n"
"\tstate.presentationActive = %d\n"
"\tstate.layerState[0].type = %hu\n"
"\tstate.hapticState[0].controllerIndex = %d\n",
state.presentationActive, state.layerState[0].type,
state.hapticState[0].controllerIndex);
// Set some state to verify on the other side
mozilla::gfx::VRSystemState systemState;
systemState.enumerationCompleted = true;
strncpy(systemState.displayState.displayName, "test from vrservice shmem",
mozilla::gfx::kVRDisplayNameMaxLen);
systemState.controllerState[1].hand = mozilla::gfx::ControllerHand::Left;
systemState.sensorState.inputFrameID = 1234567;
printf("11 svc: push system\n");
shmem.PushSystemState(systemState);
printf("12 svc: send signal\n");
::SetEvent(hEvent);
shmem.LeaveShMem();
}

View File

@ -1,14 +1,14 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// vrhostex.h
// This file declares the functions and their typedefs for functions exported
// by vrhost.dll
#pragma once
// void SampleExport();
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// vrhostex.h
// This file declares the functions and their typedefs for functions exported
// by vrhost.dll
#pragma once
// void SampleExport();
typedef void (*PFN_SAMPLE)();

View File

@ -1,416 +1,416 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef ModuloBuffer_h
#define ModuloBuffer_h
#include "mozilla/leb128iterator.h"
#include "mozilla/NotNull.h"
#include "mozilla/PowerOfTwo.h"
#include "mozilla/UniquePtr.h"
#include <limits>
#include <type_traits>
namespace mozilla {
// The ModuloBuffer class is a circular buffer that holds raw byte values, with
// data-read/write helpers.
//
// OffsetT: Type of the internal offset into the buffer of bytes, it should be
// large enough to access all bytes of the buffer. It will also be used as
// Length (in bytes) of the buffer and of any subset. Default uint32_t
// IndexT: Type of the external index, it should be large enough that overflows
// should not happen during the lifetime of the ModuloBuffer.
//
// The basic usage is to create an iterator-like object with `ReaderAt(Index)`
// or `WriterAt(Index)`, and use it to read/write data blobs. Iterators
// automatically manage the wrap-around (through "Modulo", which is effectively
// an AND-masking with the PowerOfTwo buffer size.)
//
// There is zero safety: No thread safety, no checks that iterators may be
// overwriting data that's still to be read, etc. It's up to the caller to add
// adequate checks.
// The intended use is as an underlying buffer for a safer container.
template <typename OffsetT = uint32_t, typename IndexT = uint64_t>
class ModuloBuffer {
public:
using Byte = uint8_t;
static_assert(sizeof(Byte) == 1, "ModuloBuffer::Byte must be 1 byte");
using Offset = OffsetT;
static_assert(!std::numeric_limits<Offset>::is_signed,
"ModuloBuffer::Offset must be an unsigned integral type");
using Length = Offset;
using Index = IndexT;
static_assert(!std::numeric_limits<Index>::is_signed,
"ModuloBuffer::Index must be an unsigned integral type");
static_assert(sizeof(Index) >= sizeof(Offset),
"ModuloBuffer::Index size must >= Offset");
explicit ModuloBuffer(PowerOfTwo<Length> aLength)
: mMask(aLength.Mask()), mBuffer(new Byte[aLength.Value()]) {}
PowerOfTwo<Length> BufferLength() const {
return PowerOfTwo<Length>(mMask.MaskValue() + 1);
}
// All ModuloBuffer operations should be done through this iterator, which has
// an effectively infinite range. The underlying wrapping-around is hidden.
// Use `ReaderAt(Index)` or `WriterAt(Index)` to create it.
//
// `const Iterator<...>` means the iterator itself cannot change, i.e., it
// cannot move, and only its const methods are available. Note that these
// const methods may still be used to modify the buffer contents (e.g.:
// `operator*()`, `Poke()`).
//
// `Iterator</*IsBufferConst=*/true>` means the buffer contents cannot be
// modified, i.e., write operations are forbidden, but the iterator may still
// move if non-const itself.
template <bool IsBufferConst>
class Iterator {
// Alias to const- or mutable-`ModuloBuffer` depending on `IsBufferConst`.
using ConstOrMutableBuffer =
typename std::conditional<IsBufferConst, const ModuloBuffer,
ModuloBuffer>::type;
// Implementation note about the strange enable-if's below:
// `template <bool NotIBC = !IsBufferConst> enable_if_t<NotIBC>`
// which intuitively could be simplified to:
// `enable_if_t<!IsBufferConst>`
// The former extra-templated syntax is in fact necessary to delay
// instantiation of these functions until they are actually needed.
//
// If we were just doing `enable_if_t<!IsBufferConst>`, this would only
// depend on the *class* (`ModuloBuffer<...>::Iterator`), which gets
// instantiated when a `ModuloBuffer` is created with some template
// arguments; at that point, all non-templated methods get instantiated, so
// there's no "SFINAE" happening, and `enable_if_t<...>` is actually doing
// `typename enable_if<...>::type` on the spot, but there is no `type` if
// `IsBufferConst` is true, so it just fails right away. E.g.:
// error: no type named 'type' in 'std::enable_if<false, void>';
// 'enable_if' cannot be used to disable this declaration
// note: in instantiation of template type alias 'enable_if_t'
// > std::enable_if_t<!IsBufferConst> WriteObject(const T& aObject) {
// in instantiation of template class
// 'mozilla::ModuloBuffer<...>::Iterator<true>'
// > auto it = mb.ReaderAt(1);
//
// By adding another template level `template <bool NotIsBufferConst =
// !IsBufferConst>`, the instantiation is delayed until the function is
// actually invoked somewhere, e.g. `it.Poke(...);`.
// So at that invocation point, the compiler looks for a "Poke" name in it,
// and considers potential template instantiations that could work. The
// `enable_if_t` is *now* attempted, with `NotIsBufferConst` taking its
// value from `!IsBufferConst`:
// - If `IsBufferConst` is false, `NotIsBufferConst` is true,
// `enable_if<NotIsBufferConst>` does define a `type` (`void` by default),
// so `enable_if_t` happily becomes `void`, the function exists and may be
// called.
// - Otherwise if `IsBufferConst` is true, `NotIsBufferConst` is false,
// `enable_if<NotIsBufferConst>` does *not* define a `type`, therefore
// `enable_if_t` produces an error because there is no `type`. Now "SFINAE"
// happens: This "Substitution Failure Is Not An Error" (by itself)... But
// then, there are no other functions named "Poke" as requested in the
// `it.Poke(...);` call, so we are now getting an error (can't find
// function), as expected because `it` had `IsBufferConst`==true. (But at
// least the compiler waited until this invocation attempt before outputting
// an error.)
//
// C++ is fun!
public:
// Can always copy/assign from the same kind of iterator.
Iterator(const Iterator& aRhs) = default;
Iterator& operator=(const Iterator& aRhs) = default;
// Can implicitly copy an Iterator-to-mutable (reader+writer) to
// Iterator-to-const (reader-only), but not the reverse.
template <bool IsRhsBufferConst,
typename = std::enable_if_t<(!IsRhsBufferConst) && IsBufferConst>>
MOZ_IMPLICIT Iterator(const Iterator<IsRhsBufferConst>& aRhs)
: mModuloBuffer(aRhs.mModuloBuffer), mIndex(aRhs.mIndex) {}
// Can implicitly assign from an Iterator-to-mutable (reader+writer) to
// Iterator-to-const (reader-only), but not the reverse.
template <bool IsRhsBufferConst,
typename = std::enable_if_t<(!IsRhsBufferConst) && IsBufferConst>>
Iterator& operator=(const Iterator<IsRhsBufferConst>& aRhs) {
mModuloBuffer = aRhs.mModuloBuffer;
mIndex = aRhs.mIndex;
return *this;
}
// Current location of the iterator in the `Index` range.
// Note that due to wrapping, multiple indices may effectively point at the
// same byte in the buffer.
Index CurrentIndex() const { return mIndex; }
// Location comparison in the `Index` range. I.e., two `Iterator`s may look
// unequal, but refer to the same buffer location.
// Must be on the same buffer.
bool operator==(const Iterator& aRhs) const {
MOZ_ASSERT(mModuloBuffer == aRhs.mModuloBuffer);
return mIndex == aRhs.mIndex;
}
bool operator!=(const Iterator& aRhs) const {
MOZ_ASSERT(mModuloBuffer == aRhs.mModuloBuffer);
return mIndex != aRhs.mIndex;
}
bool operator<(const Iterator& aRhs) const {
MOZ_ASSERT(mModuloBuffer == aRhs.mModuloBuffer);
return mIndex < aRhs.mIndex;
}
bool operator<=(const Iterator& aRhs) const {
MOZ_ASSERT(mModuloBuffer == aRhs.mModuloBuffer);
return mIndex <= aRhs.mIndex;
}
bool operator>(const Iterator& aRhs) const {
MOZ_ASSERT(mModuloBuffer == aRhs.mModuloBuffer);
return mIndex > aRhs.mIndex;
}
bool operator>=(const Iterator& aRhs) const {
MOZ_ASSERT(mModuloBuffer == aRhs.mModuloBuffer);
return mIndex >= aRhs.mIndex;
}
// Movement in the `Index` range.
Iterator& operator++() {
++mIndex;
return *this;
}
Iterator& operator--() {
--mIndex;
return *this;
}
Iterator& operator+=(Length aLength) {
mIndex += aLength;
return *this;
}
Iterator operator+(Length aLength) const {
return Iterator(*mModuloBuffer, mIndex + aLength);
}
Iterator& operator-=(Length aLength) {
mIndex -= aLength;
return *this;
}
Iterator operator-(Length aLength) const {
return Iterator(*mModuloBuffer, mIndex - aLength);
}
// Distance from `aRef` to here in the `Index` range.
// May be negative (as 2's complement) if `aRef > *this`.
Index operator-(const Iterator& aRef) const {
MOZ_ASSERT(mModuloBuffer == aRef.mModuloBuffer);
return mIndex - aRef.mIndex;
}
// Dereference a single byte (read-only if `IsBufferConst` is true).
std::conditional_t<IsBufferConst, const Byte&, Byte&> operator*() const {
return mModuloBuffer->mBuffer[OffsetInBuffer()];
}
// Write data (if `IsBufferConst` is false) but don't move iterator.
template <bool NotIsBufferConst = !IsBufferConst>
std::enable_if_t<NotIsBufferConst> Poke(const void* aSrc,
Length aLength) const {
// Don't allow data larger than the buffer.
MOZ_ASSERT(aLength <= mModuloBuffer->BufferLength().Value());
// Offset inside the buffer (corresponding to our Index).
Offset offset = OffsetInBuffer();
// Compute remaining bytes between this offset and the end of the buffer.
Length remaining = mModuloBuffer->BufferLength().Value() - offset;
if (MOZ_LIKELY(remaining >= aLength)) {
// Enough space to write everything before the end.
memcpy(&mModuloBuffer->mBuffer[offset], aSrc, aLength);
} else {
// Not enough space. Write as much as possible before the end.
memcpy(&mModuloBuffer->mBuffer[offset], aSrc, remaining);
// And then continue from the beginning of the buffer.
memcpy(&mModuloBuffer->mBuffer[0],
static_cast<const Byte*>(aSrc) + remaining,
(aLength - remaining));
}
}
// Write object data (if `IsBufferConst` is false) but don't move iterator.
// Note that this copies bytes from the object, with the intent to read them
// back later. Restricted to trivially-copyable types, which support this
// without Undefined Behavior!
template <typename T, bool NotIsBufferConst = !IsBufferConst>
std::enable_if_t<NotIsBufferConst> PokeObject(const T& aObject) const {
static_assert(std::is_trivially_copyable<T>::value,
"PokeObject<T> - T must be trivially copyable");
return Poke(&aObject, sizeof(T));
}
// Write data (if `IsBufferConst` is false) and move iterator ahead.
template <bool NotIsBufferConst = !IsBufferConst>
std::enable_if_t<NotIsBufferConst> Write(const void* aSrc, Length aLength) {
Poke(aSrc, aLength);
mIndex += aLength;
}
// Write object data (if `IsBufferConst` is false) and move iterator ahead.
// Note that this copies bytes from the object, with the intent to read them
// back later. Restricted to trivially-copyable types, which support this
// without Undefined Behavior!
template <typename T, bool NotIsBufferConst = !IsBufferConst>
std::enable_if_t<NotIsBufferConst> WriteObject(const T& aObject) {
static_assert(std::is_trivially_copyable<T>::value,
"WriteObject<T> - T must be trivially copyable");
return Write(&aObject, sizeof(T));
}
// Number of bytes needed to represent `aValue` in unsigned LEB128.
template <typename T>
static unsigned ULEB128Size(T aValue) {
return ::mozilla::ULEB128Size(aValue);
}
// Write number as unsigned LEB128 (if `IsBufferConst` is false) and move
// iterator ahead.
template <typename T, bool NotIsBufferConst = !IsBufferConst>
std::enable_if_t<NotIsBufferConst> WriteULEB128(T aValue) {
::mozilla::WriteULEB128(aValue, *this);
}
// Read data but don't move iterator.
void Peek(void* aDst, Length aLength) const {
// Don't allow data larger than the buffer.
MOZ_ASSERT(aLength <= mModuloBuffer->BufferLength().Value());
// Offset inside the buffer (corresponding to our Index).
Offset offset = OffsetInBuffer();
// Compute remaining bytes between this offset and the end of the buffer.
Length remaining = mModuloBuffer->BufferLength().Value() - offset;
if (MOZ_LIKELY(remaining >= aLength)) {
// Can read everything we need before the end of the buffer.
memcpy(aDst, &mModuloBuffer->mBuffer[offset], aLength);
} else {
// Read as much as possible before the end of the buffer.
memcpy(aDst, &mModuloBuffer->mBuffer[offset], remaining);
// And then continue from the beginning of the buffer.
memcpy(static_cast<Byte*>(aDst) + remaining, &mModuloBuffer->mBuffer[0],
(aLength - remaining));
}
}
// Read data into an object but don't move iterator.
// Note that this overwrites `aObject` with bytes from the buffer.
// Restricted to trivially-copyable types, which support this without
// Undefined Behavior!
template <typename T>
void PeekIntoObject(T& aObject) const {
static_assert(std::is_trivially_copyable<T>::value,
"PeekIntoObject<T> - T must be trivially copyable");
Peek(&aObject, sizeof(T));
}
// Read data as an object but don't move iterator.
// Note that this creates an default `T` first, and then overwrites it with
// bytes from the buffer. Restricted to trivially-copyable types, which
// support this without Undefined Behavior!
template <typename T>
T PeekObject() const {
static_assert(std::is_trivially_copyable<T>::value,
"PeekObject<T> - T must be trivially copyable");
T object;
PeekIntoObject(object);
return object;
}
// Read data and move iterator ahead.
void Read(void* aDst, Length aLength) {
Peek(aDst, aLength);
mIndex += aLength;
}
// Read data into an object and move iterator ahead.
// Note that this overwrites `aObject` with bytes from the buffer.
// Restricted to trivially-copyable types, which support this without
// Undefined Behavior!
template <typename T>
void ReadIntoObject(T& aObject) {
static_assert(std::is_trivially_copyable<T>::value,
"ReadIntoObject<T> - T must be trivially copyable");
Read(&aObject, sizeof(T));
}
// Read data as an object and move iterator ahead.
// Note that this creates an default `T` first, and then overwrites it with
// bytes from the buffer. Restricted to trivially-copyable types, which
// support this without Undefined Behavior!
template <typename T>
T ReadObject() {
static_assert(std::is_trivially_copyable<T>::value,
"ReadObject<T> - T must be trivially copyable");
T object;
ReadIntoObject(object);
return object;
}
// Read an unsigned LEB128 number and move iterator ahead.
template <typename T>
T ReadULEB128() {
return ::mozilla::ReadULEB128<T>(*this);
}
private:
// Only a ModuloBuffer can instantiate its iterator.
friend class ModuloBuffer;
Iterator(ConstOrMutableBuffer& aBuffer, Index aIndex)
: mModuloBuffer(WrapNotNull(&aBuffer)), mIndex(aIndex) {}
// Convert the Iterator's mIndex into an offset inside the byte buffer.
Offset OffsetInBuffer() const {
return static_cast<Offset>(mIndex) & mModuloBuffer->mMask;
}
// ModuloBuffer that this Iterator operates on.
// Using a non-null pointer instead of a reference, to allow re-assignment
// of an Iterator variable.
NotNull<ConstOrMutableBuffer*> mModuloBuffer;
// Position of this iterator in the wider `Index` range. (Will be wrapped
// around as needed when actually accessing bytes from the buffer.)
Index mIndex;
};
// Shortcut to iterator to const (read-only) data.
using Reader = Iterator<true>;
// Shortcut to iterator to non-const (read/write) data.
using Writer = Iterator<false>;
// Create an iterator to const data at the given index.
Reader ReaderAt(Index aIndex) const { return Reader(*this, aIndex); }
// Create an iterator to non-const data at the given index.
Writer WriterAt(Index aIndex) { return Writer(*this, aIndex); }
#ifdef DEBUG
void Dump() const {
Length len = BufferLength().Value();
if (len > 128) {
len = 128;
}
for (Length i = 0; i < len; ++i) {
printf("%02x ", mBuffer[i]);
}
printf("\n");
}
#endif // DEBUG
private:
// Mask used to convert an index to an offset in `mBuffer`
const PowerOfTwoMask<Offset> mMask;
// Buffer data.
UniquePtr<Byte[]> mBuffer;
};
} // namespace mozilla
#endif // ModuloBuffer_h
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef ModuloBuffer_h
#define ModuloBuffer_h
#include "mozilla/leb128iterator.h"
#include "mozilla/NotNull.h"
#include "mozilla/PowerOfTwo.h"
#include "mozilla/UniquePtr.h"
#include <limits>
#include <type_traits>
namespace mozilla {
// The ModuloBuffer class is a circular buffer that holds raw byte values, with
// data-read/write helpers.
//
// OffsetT: Type of the internal offset into the buffer of bytes, it should be
// large enough to access all bytes of the buffer. It will also be used as
// Length (in bytes) of the buffer and of any subset. Default uint32_t
// IndexT: Type of the external index, it should be large enough that overflows
// should not happen during the lifetime of the ModuloBuffer.
//
// The basic usage is to create an iterator-like object with `ReaderAt(Index)`
// or `WriterAt(Index)`, and use it to read/write data blobs. Iterators
// automatically manage the wrap-around (through "Modulo", which is effectively
// an AND-masking with the PowerOfTwo buffer size.)
//
// There is zero safety: No thread safety, no checks that iterators may be
// overwriting data that's still to be read, etc. It's up to the caller to add
// adequate checks.
// The intended use is as an underlying buffer for a safer container.
template <typename OffsetT = uint32_t, typename IndexT = uint64_t>
class ModuloBuffer {
public:
using Byte = uint8_t;
static_assert(sizeof(Byte) == 1, "ModuloBuffer::Byte must be 1 byte");
using Offset = OffsetT;
static_assert(!std::numeric_limits<Offset>::is_signed,
"ModuloBuffer::Offset must be an unsigned integral type");
using Length = Offset;
using Index = IndexT;
static_assert(!std::numeric_limits<Index>::is_signed,
"ModuloBuffer::Index must be an unsigned integral type");
static_assert(sizeof(Index) >= sizeof(Offset),
"ModuloBuffer::Index size must >= Offset");
explicit ModuloBuffer(PowerOfTwo<Length> aLength)
: mMask(aLength.Mask()), mBuffer(new Byte[aLength.Value()]) {}
PowerOfTwo<Length> BufferLength() const {
return PowerOfTwo<Length>(mMask.MaskValue() + 1);
}
// All ModuloBuffer operations should be done through this iterator, which has
// an effectively infinite range. The underlying wrapping-around is hidden.
// Use `ReaderAt(Index)` or `WriterAt(Index)` to create it.
//
// `const Iterator<...>` means the iterator itself cannot change, i.e., it
// cannot move, and only its const methods are available. Note that these
// const methods may still be used to modify the buffer contents (e.g.:
// `operator*()`, `Poke()`).
//
// `Iterator</*IsBufferConst=*/true>` means the buffer contents cannot be
// modified, i.e., write operations are forbidden, but the iterator may still
// move if non-const itself.
template <bool IsBufferConst>
class Iterator {
// Alias to const- or mutable-`ModuloBuffer` depending on `IsBufferConst`.
using ConstOrMutableBuffer =
typename std::conditional<IsBufferConst, const ModuloBuffer,
ModuloBuffer>::type;
// Implementation note about the strange enable-if's below:
// `template <bool NotIBC = !IsBufferConst> enable_if_t<NotIBC>`
// which intuitively could be simplified to:
// `enable_if_t<!IsBufferConst>`
// The former extra-templated syntax is in fact necessary to delay
// instantiation of these functions until they are actually needed.
//
// If we were just doing `enable_if_t<!IsBufferConst>`, this would only
// depend on the *class* (`ModuloBuffer<...>::Iterator`), which gets
// instantiated when a `ModuloBuffer` is created with some template
// arguments; at that point, all non-templated methods get instantiated, so
// there's no "SFINAE" happening, and `enable_if_t<...>` is actually doing
// `typename enable_if<...>::type` on the spot, but there is no `type` if
// `IsBufferConst` is true, so it just fails right away. E.g.:
// error: no type named 'type' in 'std::enable_if<false, void>';
// 'enable_if' cannot be used to disable this declaration
// note: in instantiation of template type alias 'enable_if_t'
// > std::enable_if_t<!IsBufferConst> WriteObject(const T& aObject) {
// in instantiation of template class
// 'mozilla::ModuloBuffer<...>::Iterator<true>'
// > auto it = mb.ReaderAt(1);
//
// By adding another template level `template <bool NotIsBufferConst =
// !IsBufferConst>`, the instantiation is delayed until the function is
// actually invoked somewhere, e.g. `it.Poke(...);`.
// So at that invocation point, the compiler looks for a "Poke" name in it,
// and considers potential template instantiations that could work. The
// `enable_if_t` is *now* attempted, with `NotIsBufferConst` taking its
// value from `!IsBufferConst`:
// - If `IsBufferConst` is false, `NotIsBufferConst` is true,
// `enable_if<NotIsBufferConst>` does define a `type` (`void` by default),
// so `enable_if_t` happily becomes `void`, the function exists and may be
// called.
// - Otherwise if `IsBufferConst` is true, `NotIsBufferConst` is false,
// `enable_if<NotIsBufferConst>` does *not* define a `type`, therefore
// `enable_if_t` produces an error because there is no `type`. Now "SFINAE"
// happens: This "Substitution Failure Is Not An Error" (by itself)... But
// then, there are no other functions named "Poke" as requested in the
// `it.Poke(...);` call, so we are now getting an error (can't find
// function), as expected because `it` had `IsBufferConst`==true. (But at
// least the compiler waited until this invocation attempt before outputting
// an error.)
//
// C++ is fun!
public:
// Can always copy/assign from the same kind of iterator.
Iterator(const Iterator& aRhs) = default;
Iterator& operator=(const Iterator& aRhs) = default;
// Can implicitly copy an Iterator-to-mutable (reader+writer) to
// Iterator-to-const (reader-only), but not the reverse.
template <bool IsRhsBufferConst,
typename = std::enable_if_t<(!IsRhsBufferConst) && IsBufferConst>>
MOZ_IMPLICIT Iterator(const Iterator<IsRhsBufferConst>& aRhs)
: mModuloBuffer(aRhs.mModuloBuffer), mIndex(aRhs.mIndex) {}
// Can implicitly assign from an Iterator-to-mutable (reader+writer) to
// Iterator-to-const (reader-only), but not the reverse.
template <bool IsRhsBufferConst,
typename = std::enable_if_t<(!IsRhsBufferConst) && IsBufferConst>>
Iterator& operator=(const Iterator<IsRhsBufferConst>& aRhs) {
mModuloBuffer = aRhs.mModuloBuffer;
mIndex = aRhs.mIndex;
return *this;
}
// Current location of the iterator in the `Index` range.
// Note that due to wrapping, multiple indices may effectively point at the
// same byte in the buffer.
Index CurrentIndex() const { return mIndex; }
// Location comparison in the `Index` range. I.e., two `Iterator`s may look
// unequal, but refer to the same buffer location.
// Must be on the same buffer.
bool operator==(const Iterator& aRhs) const {
MOZ_ASSERT(mModuloBuffer == aRhs.mModuloBuffer);
return mIndex == aRhs.mIndex;
}
bool operator!=(const Iterator& aRhs) const {
MOZ_ASSERT(mModuloBuffer == aRhs.mModuloBuffer);
return mIndex != aRhs.mIndex;
}
bool operator<(const Iterator& aRhs) const {
MOZ_ASSERT(mModuloBuffer == aRhs.mModuloBuffer);
return mIndex < aRhs.mIndex;
}
bool operator<=(const Iterator& aRhs) const {
MOZ_ASSERT(mModuloBuffer == aRhs.mModuloBuffer);
return mIndex <= aRhs.mIndex;
}
bool operator>(const Iterator& aRhs) const {
MOZ_ASSERT(mModuloBuffer == aRhs.mModuloBuffer);
return mIndex > aRhs.mIndex;
}
bool operator>=(const Iterator& aRhs) const {
MOZ_ASSERT(mModuloBuffer == aRhs.mModuloBuffer);
return mIndex >= aRhs.mIndex;
}
// Movement in the `Index` range.
Iterator& operator++() {
++mIndex;
return *this;
}
Iterator& operator--() {
--mIndex;
return *this;
}
Iterator& operator+=(Length aLength) {
mIndex += aLength;
return *this;
}
Iterator operator+(Length aLength) const {
return Iterator(*mModuloBuffer, mIndex + aLength);
}
Iterator& operator-=(Length aLength) {
mIndex -= aLength;
return *this;
}
Iterator operator-(Length aLength) const {
return Iterator(*mModuloBuffer, mIndex - aLength);
}
// Distance from `aRef` to here in the `Index` range.
// May be negative (as 2's complement) if `aRef > *this`.
Index operator-(const Iterator& aRef) const {
MOZ_ASSERT(mModuloBuffer == aRef.mModuloBuffer);
return mIndex - aRef.mIndex;
}
// Dereference a single byte (read-only if `IsBufferConst` is true).
std::conditional_t<IsBufferConst, const Byte&, Byte&> operator*() const {
return mModuloBuffer->mBuffer[OffsetInBuffer()];
}
// Write data (if `IsBufferConst` is false) but don't move iterator.
template <bool NotIsBufferConst = !IsBufferConst>
std::enable_if_t<NotIsBufferConst> Poke(const void* aSrc,
Length aLength) const {
// Don't allow data larger than the buffer.
MOZ_ASSERT(aLength <= mModuloBuffer->BufferLength().Value());
// Offset inside the buffer (corresponding to our Index).
Offset offset = OffsetInBuffer();
// Compute remaining bytes between this offset and the end of the buffer.
Length remaining = mModuloBuffer->BufferLength().Value() - offset;
if (MOZ_LIKELY(remaining >= aLength)) {
// Enough space to write everything before the end.
memcpy(&mModuloBuffer->mBuffer[offset], aSrc, aLength);
} else {
// Not enough space. Write as much as possible before the end.
memcpy(&mModuloBuffer->mBuffer[offset], aSrc, remaining);
// And then continue from the beginning of the buffer.
memcpy(&mModuloBuffer->mBuffer[0],
static_cast<const Byte*>(aSrc) + remaining,
(aLength - remaining));
}
}
// Write object data (if `IsBufferConst` is false) but don't move iterator.
// Note that this copies bytes from the object, with the intent to read them
// back later. Restricted to trivially-copyable types, which support this
// without Undefined Behavior!
template <typename T, bool NotIsBufferConst = !IsBufferConst>
std::enable_if_t<NotIsBufferConst> PokeObject(const T& aObject) const {
static_assert(std::is_trivially_copyable<T>::value,
"PokeObject<T> - T must be trivially copyable");
return Poke(&aObject, sizeof(T));
}
// Write data (if `IsBufferConst` is false) and move iterator ahead.
template <bool NotIsBufferConst = !IsBufferConst>
std::enable_if_t<NotIsBufferConst> Write(const void* aSrc, Length aLength) {
Poke(aSrc, aLength);
mIndex += aLength;
}
// Write object data (if `IsBufferConst` is false) and move iterator ahead.
// Note that this copies bytes from the object, with the intent to read them
// back later. Restricted to trivially-copyable types, which support this
// without Undefined Behavior!
template <typename T, bool NotIsBufferConst = !IsBufferConst>
std::enable_if_t<NotIsBufferConst> WriteObject(const T& aObject) {
static_assert(std::is_trivially_copyable<T>::value,
"WriteObject<T> - T must be trivially copyable");
return Write(&aObject, sizeof(T));
}
// Number of bytes needed to represent `aValue` in unsigned LEB128.
template <typename T>
static unsigned ULEB128Size(T aValue) {
return ::mozilla::ULEB128Size(aValue);
}
// Write number as unsigned LEB128 (if `IsBufferConst` is false) and move
// iterator ahead.
template <typename T, bool NotIsBufferConst = !IsBufferConst>
std::enable_if_t<NotIsBufferConst> WriteULEB128(T aValue) {
::mozilla::WriteULEB128(aValue, *this);
}
// Read data but don't move iterator.
void Peek(void* aDst, Length aLength) const {
// Don't allow data larger than the buffer.
MOZ_ASSERT(aLength <= mModuloBuffer->BufferLength().Value());
// Offset inside the buffer (corresponding to our Index).
Offset offset = OffsetInBuffer();
// Compute remaining bytes between this offset and the end of the buffer.
Length remaining = mModuloBuffer->BufferLength().Value() - offset;
if (MOZ_LIKELY(remaining >= aLength)) {
// Can read everything we need before the end of the buffer.
memcpy(aDst, &mModuloBuffer->mBuffer[offset], aLength);
} else {
// Read as much as possible before the end of the buffer.
memcpy(aDst, &mModuloBuffer->mBuffer[offset], remaining);
// And then continue from the beginning of the buffer.
memcpy(static_cast<Byte*>(aDst) + remaining, &mModuloBuffer->mBuffer[0],
(aLength - remaining));
}
}
// Read data into an object but don't move iterator.
// Note that this overwrites `aObject` with bytes from the buffer.
// Restricted to trivially-copyable types, which support this without
// Undefined Behavior!
template <typename T>
void PeekIntoObject(T& aObject) const {
static_assert(std::is_trivially_copyable<T>::value,
"PeekIntoObject<T> - T must be trivially copyable");
Peek(&aObject, sizeof(T));
}
// Read data as an object but don't move iterator.
// Note that this creates an default `T` first, and then overwrites it with
// bytes from the buffer. Restricted to trivially-copyable types, which
// support this without Undefined Behavior!
template <typename T>
T PeekObject() const {
static_assert(std::is_trivially_copyable<T>::value,
"PeekObject<T> - T must be trivially copyable");
T object;
PeekIntoObject(object);
return object;
}
// Read data and move iterator ahead.
void Read(void* aDst, Length aLength) {
Peek(aDst, aLength);
mIndex += aLength;
}
// Read data into an object and move iterator ahead.
// Note that this overwrites `aObject` with bytes from the buffer.
// Restricted to trivially-copyable types, which support this without
// Undefined Behavior!
template <typename T>
void ReadIntoObject(T& aObject) {
static_assert(std::is_trivially_copyable<T>::value,
"ReadIntoObject<T> - T must be trivially copyable");
Read(&aObject, sizeof(T));
}
// Read data as an object and move iterator ahead.
// Note that this creates an default `T` first, and then overwrites it with
// bytes from the buffer. Restricted to trivially-copyable types, which
// support this without Undefined Behavior!
template <typename T>
T ReadObject() {
static_assert(std::is_trivially_copyable<T>::value,
"ReadObject<T> - T must be trivially copyable");
T object;
ReadIntoObject(object);
return object;
}
// Read an unsigned LEB128 number and move iterator ahead.
template <typename T>
T ReadULEB128() {
return ::mozilla::ReadULEB128<T>(*this);
}
private:
// Only a ModuloBuffer can instantiate its iterator.
friend class ModuloBuffer;
Iterator(ConstOrMutableBuffer& aBuffer, Index aIndex)
: mModuloBuffer(WrapNotNull(&aBuffer)), mIndex(aIndex) {}
// Convert the Iterator's mIndex into an offset inside the byte buffer.
Offset OffsetInBuffer() const {
return static_cast<Offset>(mIndex) & mModuloBuffer->mMask;
}
// ModuloBuffer that this Iterator operates on.
// Using a non-null pointer instead of a reference, to allow re-assignment
// of an Iterator variable.
NotNull<ConstOrMutableBuffer*> mModuloBuffer;
// Position of this iterator in the wider `Index` range. (Will be wrapped
// around as needed when actually accessing bytes from the buffer.)
Index mIndex;
};
// Shortcut to iterator to const (read-only) data.
using Reader = Iterator<true>;
// Shortcut to iterator to non-const (read/write) data.
using Writer = Iterator<false>;
// Create an iterator to const data at the given index.
Reader ReaderAt(Index aIndex) const { return Reader(*this, aIndex); }
// Create an iterator to non-const data at the given index.
Writer WriterAt(Index aIndex) { return Writer(*this, aIndex); }
#ifdef DEBUG
void Dump() const {
Length len = BufferLength().Value();
if (len > 128) {
len = 128;
}
for (Length i = 0; i < len; ++i) {
printf("%02x ", mBuffer[i]);
}
printf("\n");
}
#endif // DEBUG
private:
// Mask used to convert an index to an offset in `mBuffer`
const PowerOfTwoMask<Offset> mMask;
// Buffer data.
UniquePtr<Byte[]> mBuffer;
};
} // namespace mozilla
#endif // ModuloBuffer_h

View File

@ -1,147 +1,147 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// LEB128 utilities that can read/write unsigned LEB128 numbers from/to
// iterators.
//
// LEB128 = Little Endian Base 128, where small numbers take few bytes, but
// large numbers are still allowed, which is ideal when serializing numbers that
// are likely to be small.
// Each byte contains 7 bits from the number, starting at the "little end", the
// top bit is 0 for the last byte, 1 otherwise.
// Numbers 0-127 only take 1 byte. 128-16383 take 2 bytes. Etc.
//
// Iterators only need to provide:
// - `*it` to return a reference to the next byte to be read from or written to.
// - `++it` to advance the iterator after a byte is written.
//
// The caller must always provide sufficient space to write any number, by:
// - pre-allocating a large enough buffer, or
// - allocating more space when `++it` reaches the end and/or `*it` is invoked
// after the end, or
// - moving the underlying pointer to an appropriate location (e.g., wrapping
// around a circular buffer).
// The caller must also provide enough bytes to read a full value (i.e., at
// least one byte should have its top bit unset), and a type large enough to
// hold the stored value.
//
// Note: There are insufficient checks for validity! These functions are
// intended to be used together, i.e., the user should only `ReadULEB128()` from
// a sufficiently-large buffer that the same user filled with `WriteULEB128()`.
// Using with externally-sourced data (e.g., DWARF) is *not* recommended.
//
// https://en.wikipedia.org/wiki/LEB128
#ifndef leb128iterator_h
#define leb128iterator_h
#include <climits>
#include <cstdint>
#include <limits>
#include <type_traits>
namespace mozilla {
// Number of bytes needed to represent `aValue`.
template <typename T>
constexpr uint_fast8_t ULEB128Size(T aValue) {
static_assert(!std::numeric_limits<T>::is_signed,
"ULEB128Size only takes unsigned types");
// We need one output byte per 7 bits of non-zero value. So we just remove
// 7 least significant bits at a time until the value becomes zero.
// Note the special case of 0, which still needs 1 output byte; this is done
// by starting the first loop before we check for 0.
uint_fast8_t size = 0;
for (;;) {
size += 1;
aValue >>= 7;
// Expecting small values, so it should be more likely that `aValue == 0`.
if (MOZ_LIKELY(aValue == 0)) {
return size;
}
}
}
// Maximum number of bytes needed to represent any value of type `T`.
template <typename T>
constexpr uint_fast8_t ULEB128MaxSize() {
return ULEB128Size<T>(std::numeric_limits<T>::max());
}
// Write `aValue` in LEB128 to `aIterator`.
// The iterator will be moved past the last byte.
template <typename T, typename It>
void WriteULEB128(T aValue, It& aIterator) {
static_assert(!std::numeric_limits<T>::is_signed,
"WriteULEB128 only takes unsigned types");
using IteratorValue = std::remove_reference_t<decltype(*aIterator)>;
static_assert(sizeof(IteratorValue) == 1,
"WriteULEB128 expects an iterator to single bytes");
// 0. Don't test for 0 yet, as we want to output one byte for it.
for (;;) {
// 1. Extract the 7 least significant bits.
const uint_fast8_t byte = aValue & 0x7Fu;
// 2. Remove them from `aValue`.
aValue >>= 7;
// 3. Write the 7 bits, and set the 8th bit if `aValue` is not 0 yet
// (meaning there will be more bytes after this one.)
// Expecting small values, so it should be more likely that `aValue == 0`.
// Note: No absolute need to force-cast to IteratorValue, because we have
// only changed the bottom 8 bits above. However the compiler could warn
// about a narrowing conversion from potentially-multibyte uint_fast8_t down
// to whatever single-byte type `*iterator* expects, so we make it explicit.
*aIterator = static_cast<IteratorValue>(
MOZ_LIKELY(aValue == 0) ? byte : (byte | 0x80u));
// 4. Always advance the iterator to the next byte.
++aIterator;
// 5. We're done if `aValue` is 0.
// Expecting small values, so it should be more likely that `aValue == 0`.
if (MOZ_LIKELY(aValue == 0)) {
return;
}
}
}
// Read an LEB128 value from `aIterator`.
// The iterator will be moved past the last byte.
template <typename T, typename It>
T ReadULEB128(It& aIterator) {
static_assert(!std::numeric_limits<T>::is_signed,
"ReadULEB128 must return an unsigned type");
using IteratorValue = std::remove_reference_t<decltype(*aIterator)>;
static_assert(sizeof(IteratorValue) == 1,
"ReadULEB128 expects an iterator to single bytes");
// Incoming bits will be added to `result`...
T result = 0;
// ... starting with the least significant bits.
uint_fast8_t shift = 0;
for (;;) {
// 1. Read one byte from the iterator.
// `static_cast` just in case IteratorValue is not implicitly convertible to
// uint_fast8_t. It wouldn't matter if the sign was extended, we're only
// dealing with the bottom 8 bits below.
const uint_fast8_t byte = static_cast<uint_fast8_t>(*aIterator);
// 2. Always advance the iterator.
++aIterator;
// 3. Extract the 7 bits of value, and shift them in place into `result`.
result |= static_cast<T>(byte & 0x7fu) << shift;
// 4. If the 8th bit is *not* set, this was the last byte.
// Expecting small values, so it should be more likely that the bit is off.
if (MOZ_LIKELY((byte & 0x80u) == 0)) {
return result;
}
// There are more bytes to read.
// 5. Next byte will contain more significant bits above the past 7.
shift += 7;
// Safety check that we're not going to shift by >= than the type size,
// which is Undefined Behavior in C++.
MOZ_ASSERT(shift < CHAR_BIT * sizeof(T));
}
}
} // namespace mozilla
#endif // leb128iterator_h
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// LEB128 utilities that can read/write unsigned LEB128 numbers from/to
// iterators.
//
// LEB128 = Little Endian Base 128, where small numbers take few bytes, but
// large numbers are still allowed, which is ideal when serializing numbers that
// are likely to be small.
// Each byte contains 7 bits from the number, starting at the "little end", the
// top bit is 0 for the last byte, 1 otherwise.
// Numbers 0-127 only take 1 byte. 128-16383 take 2 bytes. Etc.
//
// Iterators only need to provide:
// - `*it` to return a reference to the next byte to be read from or written to.
// - `++it` to advance the iterator after a byte is written.
//
// The caller must always provide sufficient space to write any number, by:
// - pre-allocating a large enough buffer, or
// - allocating more space when `++it` reaches the end and/or `*it` is invoked
// after the end, or
// - moving the underlying pointer to an appropriate location (e.g., wrapping
// around a circular buffer).
// The caller must also provide enough bytes to read a full value (i.e., at
// least one byte should have its top bit unset), and a type large enough to
// hold the stored value.
//
// Note: There are insufficient checks for validity! These functions are
// intended to be used together, i.e., the user should only `ReadULEB128()` from
// a sufficiently-large buffer that the same user filled with `WriteULEB128()`.
// Using with externally-sourced data (e.g., DWARF) is *not* recommended.
//
// https://en.wikipedia.org/wiki/LEB128
#ifndef leb128iterator_h
#define leb128iterator_h
#include <climits>
#include <cstdint>
#include <limits>
#include <type_traits>
namespace mozilla {
// Number of bytes needed to represent `aValue`.
template <typename T>
constexpr uint_fast8_t ULEB128Size(T aValue) {
static_assert(!std::numeric_limits<T>::is_signed,
"ULEB128Size only takes unsigned types");
// We need one output byte per 7 bits of non-zero value. So we just remove
// 7 least significant bits at a time until the value becomes zero.
// Note the special case of 0, which still needs 1 output byte; this is done
// by starting the first loop before we check for 0.
uint_fast8_t size = 0;
for (;;) {
size += 1;
aValue >>= 7;
// Expecting small values, so it should be more likely that `aValue == 0`.
if (MOZ_LIKELY(aValue == 0)) {
return size;
}
}
}
// Maximum number of bytes needed to represent any value of type `T`.
template <typename T>
constexpr uint_fast8_t ULEB128MaxSize() {
return ULEB128Size<T>(std::numeric_limits<T>::max());
}
// Write `aValue` in LEB128 to `aIterator`.
// The iterator will be moved past the last byte.
template <typename T, typename It>
void WriteULEB128(T aValue, It& aIterator) {
static_assert(!std::numeric_limits<T>::is_signed,
"WriteULEB128 only takes unsigned types");
using IteratorValue = std::remove_reference_t<decltype(*aIterator)>;
static_assert(sizeof(IteratorValue) == 1,
"WriteULEB128 expects an iterator to single bytes");
// 0. Don't test for 0 yet, as we want to output one byte for it.
for (;;) {
// 1. Extract the 7 least significant bits.
const uint_fast8_t byte = aValue & 0x7Fu;
// 2. Remove them from `aValue`.
aValue >>= 7;
// 3. Write the 7 bits, and set the 8th bit if `aValue` is not 0 yet
// (meaning there will be more bytes after this one.)
// Expecting small values, so it should be more likely that `aValue == 0`.
// Note: No absolute need to force-cast to IteratorValue, because we have
// only changed the bottom 8 bits above. However the compiler could warn
// about a narrowing conversion from potentially-multibyte uint_fast8_t down
// to whatever single-byte type `*iterator* expects, so we make it explicit.
*aIterator = static_cast<IteratorValue>(
MOZ_LIKELY(aValue == 0) ? byte : (byte | 0x80u));
// 4. Always advance the iterator to the next byte.
++aIterator;
// 5. We're done if `aValue` is 0.
// Expecting small values, so it should be more likely that `aValue == 0`.
if (MOZ_LIKELY(aValue == 0)) {
return;
}
}
}
// Read an LEB128 value from `aIterator`.
// The iterator will be moved past the last byte.
template <typename T, typename It>
T ReadULEB128(It& aIterator) {
static_assert(!std::numeric_limits<T>::is_signed,
"ReadULEB128 must return an unsigned type");
using IteratorValue = std::remove_reference_t<decltype(*aIterator)>;
static_assert(sizeof(IteratorValue) == 1,
"ReadULEB128 expects an iterator to single bytes");
// Incoming bits will be added to `result`...
T result = 0;
// ... starting with the least significant bits.
uint_fast8_t shift = 0;
for (;;) {
// 1. Read one byte from the iterator.
// `static_cast` just in case IteratorValue is not implicitly convertible to
// uint_fast8_t. It wouldn't matter if the sign was extended, we're only
// dealing with the bottom 8 bits below.
const uint_fast8_t byte = static_cast<uint_fast8_t>(*aIterator);
// 2. Always advance the iterator.
++aIterator;
// 3. Extract the 7 bits of value, and shift them in place into `result`.
result |= static_cast<T>(byte & 0x7fu) << shift;
// 4. If the 8th bit is *not* set, this was the last byte.
// Expecting small values, so it should be more likely that the bit is off.
if (MOZ_LIKELY((byte & 0x80u) == 0)) {
return result;
}
// There are more bytes to read.
// 5. Next byte will contain more significant bits above the past 7.
shift += 7;
// Safety check that we're not going to shift by >= than the type size,
// which is Undefined Behavior in C++.
MOZ_ASSERT(shift < CHAR_BIT * sizeof(T));
}
}
} // namespace mozilla
#endif // leb128iterator_h