mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 07:13:20 +00:00
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:
parent
2ff79fb0d5
commit
ddf2513ea7
@ -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
|
||||
|
@ -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 */
|
||||
|
130
gfx/vr/VRShMem.h
130
gfx/vr/VRShMem.h
@ -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
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
@ -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();
|
||||
}
|
@ -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)();
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user