Global Metrics

path: .metrics.nom.functions
old: 2.0
new: 6.0

path: .metrics.nom.total
old: 2.0
new: 6.0

path: .metrics.cognitive.sum
old: 2.0
new: 35.0

path: .metrics.cognitive.average
old: 1.0
new: 5.833333333333333

path: .metrics.cyclomatic.average
old: 1.25
new: 3.4166666666666665

path: .metrics.cyclomatic.sum
old: 5.0
new: 41.0

path: .metrics.nargs.sum
old: 4.0
new: 12.0

path: .metrics.nexits.sum
old: 2.0
new: 14.0

path: .metrics.nexits.average
old: 1.0
new: 2.3333333333333335

path: .metrics.loc.lloc
old: 8.0
new: 58.0

path: .metrics.loc.cloc
old: 6.0
new: 70.0

path: .metrics.loc.sloc
old: 38.0
new: 521.0

path: .metrics.loc.ploc
old: 26.0
new: 346.0

path: .metrics.loc.blank
old: 6.0
new: 105.0

path: .metrics.halstead.difficulty
old: 9.0
new: 34.61666666666667

path: .metrics.halstead.n1
old: 12.0
new: 31.0

path: .metrics.halstead.purity_ratio
old: 1.911445014258101
new: 1.751178017123569

path: .metrics.halstead.bugs
old: 0.1053348789179992
new: 1.748569413319447

path: .metrics.halstead.level
old: 0.1111111111111111
new: 0.02888781896966779

path: .metrics.halstead.time
old: 312.08125051722124
new: 21107.349088410796

path: .metrics.halstead.length
old: 113.0
new: 1333.0

path: .metrics.halstead.volume
old: 624.1625010344425
new: 10975.415029120682

path: .metrics.halstead.N1
old: 62.0
new: 730.0

path: .metrics.halstead.N2
old: 51.0
new: 603.0

path: .metrics.halstead.n2
old: 34.0
new: 270.0

path: .metrics.halstead.effort
old: 5617.462509309982
new: 379932.2835913943

path: .metrics.halstead.vocabulary
old: 46.0
new: 301.0

path: .metrics.halstead.estimated_program_length
old: 215.99328661116544
new: 2334.3202968257174

path: .metrics.mi.mi_visual_studio
old: 45.29343175316955
new: 6.929299092110443

path: .metrics.mi.mi_sei
old: 65.41942323929804
new: -27.54044361920933

path: .metrics.mi.mi_original
old: 77.45176829791993
new: 11.849101447508858

Spaces Data

Minimal test - lines (118, 519)

path: .spaces[0].metrics.cognitive.sum
old: 2.0
new: 35.0

path: .spaces[0].metrics.cognitive.average
old: 1.0
new: 5.833333333333333

path: .spaces[0].metrics.cyclomatic.average
old: 1.3333333333333333
new: 3.636363636363636

path: .spaces[0].metrics.cyclomatic.sum
old: 4.0
new: 40.0

path: .spaces[0].metrics.loc.cloc
old: 0.0
new: 60.0

path: .spaces[0].metrics.loc.lloc
old: 8.0
new: 58.0

path: .spaces[0].metrics.loc.ploc
old: 22.0
new: 307.0

path: .spaces[0].metrics.loc.sloc
old: 26.0
new: 402.0

path: .spaces[0].metrics.loc.blank
old: 4.0
new: 35.0

path: .spaces[0].metrics.halstead.bugs
old: 0.10417542459227112
new: 1.453013230916801

path: .spaces[0].metrics.halstead.estimated_program_length
old: 190.22626787690945
new: 2192.140212021002

path: .spaces[0].metrics.halstead.purity_ratio
old: 1.745195118136784
new: 1.907867895579636

path: .spaces[0].metrics.halstead.difficulty
old: 9.4
new: 30.69607843137255

path: .spaces[0].metrics.halstead.N1
old: 62.0
new: 644.0

path: .spaces[0].metrics.halstead.length
old: 109.0
new: 1149.0

path: .spaces[0].metrics.halstead.n1
old: 12.0
new: 31.0

path: .spaces[0].metrics.halstead.N2
old: 47.0
new: 505.0

path: .spaces[0].metrics.halstead.vocabulary
old: 42.0
new: 286.0

path: .spaces[0].metrics.halstead.time
old: 306.94269063217325
new: 15988.721226370182

path: .spaces[0].metrics.halstead.n2
old: 30.0
new: 255.0

path: .spaces[0].metrics.halstead.volume
old: 587.7625990828849
new: 9375.69216595837

path: .spaces[0].metrics.halstead.effort
old: 5524.968431379119
new: 287796.9820746633

path: .spaces[0].metrics.halstead.level
old: 0.10638297872340426
new: 0.03257745129351645

path: .spaces[0].metrics.nexits.average
old: 1.0
new: 2.3333333333333335

path: .spaces[0].metrics.nexits.sum
old: 2.0
new: 14.0

path: .spaces[0].metrics.nom.total
old: 2.0
new: 6.0

path: .spaces[0].metrics.nom.functions
old: 2.0
new: 6.0

path: .spaces[0].metrics.mi.mi_visual_studio
old: 49.20582213029712
new: 9.999369960700664

path: .spaces[0].metrics.mi.mi_original
old: 84.14195584280807
new: 17.098922632798136

path: .spaces[0].metrics.mi.mi_sei
old: 46.097609870722465
new: -18.789103569057204

path: .spaces[0].metrics.nargs.sum
old: 4.0
new: 12.0

Code

namespace mozilla {
namespace widget {

enum class OperatingSystem : uint8_t {
  Unknown,
  Windows,
  WindowsXP,
  WindowsServer2003,
  WindowsVista,
  Windows7,
  Windows8,
  Windows8_1,
  Windows10,
  RecentWindows10,
  NotRecentWindows10,
  Linux,
  OSX,
  OSX10_5,
  OSX10_6,
  OSX10_7,
  OSX10_8,
  OSX10_9,
  OSX10_10,
  OSX10_11,
  OSX10_12,
  OSX10_13,
  OSX10_14,
  OSX10_15,
  OSX11_0,
  Android,
  Ios
};

enum VersionComparisonOp {
  DRIVER_LESS_THAN,                    // driver <  version
  DRIVER_BUILD_ID_LESS_THAN,           // driver build id <  version
  DRIVER_LESS_THAN_OR_EQUAL,           // driver <= version
  DRIVER_BUILD_ID_LESS_THAN_OR_EQUAL,  // driver build id <= version
  DRIVER_GREATER_THAN,                 // driver >  version
  DRIVER_GREATER_THAN_OR_EQUAL,        // driver >= version
  DRIVER_EQUAL,                        // driver == version
  DRIVER_NOT_EQUAL,                    // driver != version
  DRIVER_BETWEEN_EXCLUSIVE,        // driver > version && driver < versionMax
  DRIVER_BETWEEN_INCLUSIVE,        // driver >= version && driver <= versionMax
  DRIVER_BETWEEN_INCLUSIVE_START,  // driver >= version && driver < versionMax
  DRIVER_COMPARISON_IGNORED
};

enum class DeviceFamily : uint8_t {
  All,
  IntelAll,
  NvidiaAll,
  AtiAll,
  MicrosoftAll,
  ParallelsAll,
  QualcommAll,
  AppleAll,
  AmazonAll,
  IntelGMA500,
  IntelGMA900,
  IntelGMA950,
  IntelGMA3150,
  IntelGMAX3000,
  IntelGMAX4500HD,
  IntelHDGraphicsToIvyBridge,
  IntelHDGraphicsToSandyBridge,
  IntelHaswell,
  IntelSandyBridge,
  IntelHD520,
  IntelMobileHDGraphics,
  NvidiaBlockD3D9Layers,
  RadeonX1000,
  RadeonCaicos,
  Geforce7300GT,
  Nvidia310M,
  Nvidia8800GTS,
  Bug1137716,
  Bug1116812,
  Bug1155608,
  Bug1207665,
  Bug1447141,
  AmdR600,
  NvidiaBlockWebRender,
  NvidiaRolloutWebRender,
  IntelRolloutWebRender,
  IntelModernRolloutWebRender,
  AtiRolloutWebRender,

  Max
};

enum class DeviceVendor : uint8_t {
  All,  // There is an assumption that this is the first enum
  Intel,
  NVIDIA,
  ATI,
  Microsoft,
  Parallels,
  VMWare,
  VirtualBox,
  Qualcomm,
  MicrosoftBasic,
  MicrosoftHyperV,
  Apple,
  Amazon,

  Max
};

enum DriverVendor : uint8_t {
  All,  // There is an assumption that this is the first enum
  // Wildcard for all Mesa drivers.
  MesaAll,
  // Note that the following list of Mesa drivers is not comprehensive; we pull
  // the DRI driver at runtime. These drivers are provided for convenience when
  // populating the local blocklist.
  MesaLLVMPipe,
  MesaSoftPipe,
  MesaSWRast,
  // AMD
  MesaR600,
  // Nouveau: Open-source nvidia
  MesaNouveau,
  // A generic ID to be provided when we can't determine the DRI driver on Mesa.
  MesaUnknown,
  // Wildcard for all non-Mesa drivers.
  NonMesaAll,
  // Wildcard for all hardware Mesa drivers.
  HardwareMesaAll,
  // Wildcard for all software Mesa drivers.
  SoftwareMesaAll,

  Max
};

enum class DesktopEnvironment : uint8_t {
  All,  // There is an assumption that this is the first enum
  GNOME,
  KDE,
  XFCE,
  Cinnamon,
  Enlightenment,
  LXDE,
  Openbox,
  i3,
  Mate,
  Unity,
  Pantheon,
  LXQT,
  Deepin,
  Dwm,
  Budgie,
  Unknown,
  Max
};

enum class WindowProtocol : uint8_t {
  All,  // There is an assumption that this is the first enum
  X11,
  XWayland,
  Wayland,
  WaylandDRM,
  // Wildcard for all Wayland variants, excluding XWayland.
  WaylandAll,
  // Wildcard for all X11 variants, including XWayland.
  X11All,
  Max
};

enum class BatteryStatus : uint8_t { All, Present, None };

enum class ScreenSizeStatus : uint8_t {
  All,
  Small,           // <= 1900x1200
  SmallAndMedium,  // <= 3440x1440
  Medium,          // <= 3440x1440 && > 1900x1200
  MediumAndLarge,  // >1900x1200
  Large            // > 3440x1440
};

/* Array of devices to match, or an empty array for all devices */
class GfxDeviceFamily final {
 public:
  GfxDeviceFamily() = default;

  void Append(const nsAString& aDeviceId);
  void AppendRange(int32_t aBeginDeviceId, int32_t aEndDeviceId);

  bool IsEmpty() const { return mIds.IsEmpty() && mRanges.IsEmpty(); }

  nsresult Contains(nsAString& aDeviceId) const;

 private:
  struct DeviceRange {
    int32_t mBegin;
    int32_t mEnd;
  };

  CopyableTArray mIds;
  CopyableTArray mRanges;
};

struct GfxDriverInfo {
  // If |ownDevices| is true, you are transferring ownership of the devices
  // array, and it will be deleted when this GfxDriverInfo is destroyed.
  GfxDriverInfo(OperatingSystem os, ScreenSizeStatus aScreen,
                BatteryStatus aBattery, const nsAString& desktopEnv,
                const nsAString& windowProtocol, const nsAString& vendor,
                const nsAString& driverVendor, GfxDeviceFamily* devices,
                int32_t feature, int32_t featureStatus, VersionComparisonOp op,
                uint64_t driverVersion, const char* ruleId,
                const char* suggestedVersion = nullptr, bool ownDevices = false,
                bool gpu2 = false);

  GfxDriverInfo();
  GfxDriverInfo(const GfxDriverInfo&);
  ~GfxDriverInfo();

  OperatingSystem mOperatingSystem;
  uint32_t mOperatingSystemVersion;
  ScreenSizeStatus mScreen;
  BatteryStatus mBattery;
  nsString mDesktopEnvironment;
  nsString mWindowProtocol;

  nsString mAdapterVendor;
  nsString mDriverVendor;

  const GfxDeviceFamily* mDevices;

  // Whether the mDevices array should be deleted when this structure is
  // deallocated. False by default.
  bool mDeleteDevices;

  /* A feature from nsIGfxInfo, or all features */
  int32_t mFeature;
  static int32_t allFeatures;

  /* A feature status from nsIGfxInfo */
  int32_t mFeatureStatus;

  VersionComparisonOp mComparisonOp;

  /* versions are assumed to be A.B.C.D packed as 0xAAAABBBBCCCCDDDD */
  uint64_t mDriverVersion;
  uint64_t mDriverVersionMax;
  static uint64_t allDriverVersions;

  const char* mSuggestedVersion;
  nsCString mRuleId;

  static const GfxDeviceFamily* GetDeviceFamily(DeviceFamily id);
  static GfxDeviceFamily*
      sDeviceFamilies[static_cast(DeviceFamily::Max)];

  static const nsAString& GetDesktopEnvironment(DesktopEnvironment id);
  static nsAString*
      sDesktopEnvironment[static_cast(DesktopEnvironment::Max)];

  static const nsAString& GetWindowProtocol(WindowProtocol id);
  static nsAString* sWindowProtocol[static_cast(WindowProtocol::Max)];

  static const nsAString& GetDeviceVendor(DeviceVendor id);
  static const nsAString& GetDeviceVendor(DeviceFamily id);
  static nsAString* sDeviceVendors[static_cast(DeviceVendor::Max)];

  static const nsAString& GetDriverVendor(DriverVendor id);
  static nsAString* sDriverVendors[static_cast(DriverVendor::Max)];

  nsString mModel, mHardware, mProduct, mManufacturer;

  bool mGpu2;
};

#define GFX_DRIVER_VERSION(a, b, c, d)                               \
  ((uint64_t(a) << 48) | (uint64_t(b) << 32) | (uint64_t(c) << 16) | \
   uint64_t(d))

inline uint64_t V(uint32_t a, uint32_t b, uint32_t c, uint32_t d) {
  // We make sure every driver number is padded by 0s, this will allow us the
  // easiest 'compare as if decimals' approach. See ParseDriverVersion for a
  // more extensive explanation of this approach.
  while (b > 0 && b < 1000) {
    b *= 10;
  }
  while (c > 0 && c < 1000) {
    c *= 10;
  }
  while (d > 0 && d < 1000) {
    d *= 10;
  }
  return GFX_DRIVER_VERSION(a, b, c, d);
}

// All destination string storage needs to have at least 5 bytes available.
inline bool SplitDriverVersion(const char* aSource, char* aAStr, char* aBStr,
                               char* aCStr, char* aDStr) {
  // sscanf doesn't do what we want here to we parse this manually.
  int len = strlen(aSource);

  // This "4" is hardcoded in a few places, including once as a 3.
  char* dest[4] = {aAStr, aBStr, aCStr, aDStr};
  unsigned destIdx = 0;
  unsigned destPos = 0;

  for (int i = 0; i < len; i++) {
    if (destIdx >= 4) {
      // Invalid format found. Ensure we don't access dest beyond bounds.
      return false;
    }

    if (aSource[i] == '.') {
      MOZ_ASSERT(destIdx < 4 && destPos <= 4);
      dest[destIdx++][destPos] = 0;
      destPos = 0;
      continue;
    }

    if (destPos > 3) {
      // Ignore more than 4 chars. Ensure we never access dest[destIdx]
      // beyond its bounds.
      continue;
    }

    MOZ_ASSERT(destIdx < 4 && destPos < 4);
    dest[destIdx][destPos++] = aSource[i];
  }

  // Take care of the trailing period
  if (destIdx >= 4) {
    return false;
  }

  // Add last terminator.
  MOZ_ASSERT(destIdx < 4 && destPos <= 4);
  dest[destIdx][destPos] = 0;

  if (destIdx != 3) {
    return false;
  }
  return true;
}

// This allows us to pad driver version 'substrings' with 0s, this
// effectively allows us to treat the version numbers as 'decimals'. This is
// a little strange but this method seems to do the right thing for all
// different vendor's driver strings. i.e. .98 will become 9800, which is
// larger than .978 which would become 9780.
inline void PadDriverDecimal(char* aString) {
  for (int i = 0; i < 4; i++) {
    if (!aString[i]) {
      for (int c = i; c < 4; c++) {
        aString[c] = '0';
      }
      break;
    }
  }
  aString[4] = 0;
}

inline bool ParseDriverVersion(const nsAString& aVersion,
                               uint64_t* aNumericVersion) {
  *aNumericVersion = 0;

#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
  int a, b, c, d;
  char aStr[8], bStr[8], cStr[8], dStr[8];
  /* honestly, why do I even bother */
  if (!SplitDriverVersion(NS_LossyConvertUTF16toASCII(aVersion).get(), aStr,
                          bStr, cStr, dStr))
    return false;

  PadDriverDecimal(bStr);
  PadDriverDecimal(cStr);
  PadDriverDecimal(dStr);

  a = atoi(aStr);
  b = atoi(bStr);
  c = atoi(cStr);
  d = atoi(dStr);

  if (a < 0 || a > 0xffff) return false;
  if (b < 0 || b > 0xffff) return false;
  if (c < 0 || c > 0xffff) return false;
  if (d < 0 || d > 0xffff) return false;

  *aNumericVersion = GFX_DRIVER_VERSION(a, b, c, d);
  MOZ_ASSERT(*aNumericVersion != GfxDriverInfo::allDriverVersions);
  return true;
#elif defined(ANDROID)
  // Can't use aVersion.ToInteger() because that's not compiled into our code
  // unless we have XPCOM_GLUE_AVOID_NSPR disabled.
  *aNumericVersion = atoi(NS_LossyConvertUTF16toASCII(aVersion).get());
  MOZ_ASSERT(*aNumericVersion != GfxDriverInfo::allDriverVersions);
  return true;
#else
  return false;
#endif
}

}  // namespace widget
}  // namespace mozilla

Minimal test - lines (119, 518)

path: .spaces[0].spaces[0].metrics.halstead.length
old: 16.0
new: 1147.0

path: .spaces[0].spaces[0].metrics.halstead.difficulty
old: 4.9
new: 30.755905511811022

path: .spaces[0].spaces[0].metrics.halstead.time
old: 15.614503336474373
new: 15982.095059251653

path: .spaces[0].spaces[0].metrics.halstead.purity_ratio
old: 1.9538203080525025
new: 1.90296948218145

path: .spaces[0].spaces[0].metrics.halstead.level
old: 0.2040816326530612
new: 0.032514080901177675

path: .spaces[0].spaces[0].metrics.halstead.estimated_program_length
old: 31.26112492884004
new: 2182.705996062123

path: .spaces[0].spaces[0].metrics.halstead.N2
old: 7.0
new: 504.0

path: .spaces[0].spaces[0].metrics.halstead.bugs
old: 0.01430251895140066
new: 1.452611757340876

path: .spaces[0].spaces[0].metrics.halstead.N1
old: 9.0
new: 643.0

path: .spaces[0].spaces[0].metrics.halstead.n1
old: 7.0
new: 31.0

path: .spaces[0].spaces[0].metrics.halstead.vocabulary
old: 12.0
new: 285.0

path: .spaces[0].spaces[0].metrics.halstead.volume
old: 57.3594000115385
new: 9353.576371082763

path: .spaces[0].spaces[0].metrics.halstead.effort
old: 281.0610600565387
new: 287677.71106652974

path: .spaces[0].spaces[0].metrics.halstead.n2
old: 5.0
new: 254.0

path: .spaces[0].spaces[0].metrics.mi.mi_original
old: 127.25548032168658
new: 17.42200128345749

path: .spaces[0].spaces[0].metrics.mi.mi_sei
old: 107.991818253432
new: -18.57067482172422

path: .spaces[0].spaces[0].metrics.mi.mi_visual_studio
old: 74.41840954484594
new: 10.188304844127186

path: .spaces[0].spaces[0].metrics.loc.lloc
old: 2.0
new: 58.0

path: .spaces[0].spaces[0].metrics.loc.sloc
old: 4.0
new: 400.0

path: .spaces[0].spaces[0].metrics.loc.blank
old: 0.0
new: 36.0

path: .spaces[0].spaces[0].metrics.loc.ploc
old: 4.0
new: 305.0

path: .spaces[0].spaces[0].metrics.loc.cloc
old: 0.0
new: 59.0

path: .spaces[0].spaces[0].metrics.cognitive.average
old: 0.0
new: 5.833333333333333

path: .spaces[0].spaces[0].metrics.cognitive.sum
old: 0.0
new: 35.0

path: .spaces[0].spaces[0].metrics.nargs.sum
old: 1.0
new: 12.0

path: .spaces[0].spaces[0].metrics.nargs.average
old: 1.0
new: 2.0

path: .spaces[0].spaces[0].metrics.cyclomatic.sum
old: 1.0
new: 39.0

path: .spaces[0].spaces[0].metrics.cyclomatic.average
old: 1.0
new: 3.9

path: .spaces[0].spaces[0].metrics.nom.total
old: 1.0
new: 6.0

path: .spaces[0].spaces[0].metrics.nom.functions
old: 1.0
new: 6.0

path: .spaces[0].spaces[0].metrics.nexits.average
old: 1.0
new: 2.3333333333333335

path: .spaces[0].spaces[0].metrics.nexits.sum
old: 1.0
new: 14.0

Code

namespace widget {

enum class OperatingSystem : uint8_t {
  Unknown,
  Windows,
  WindowsXP,
  WindowsServer2003,
  WindowsVista,
  Windows7,
  Windows8,
  Windows8_1,
  Windows10,
  RecentWindows10,
  NotRecentWindows10,
  Linux,
  OSX,
  OSX10_5,
  OSX10_6,
  OSX10_7,
  OSX10_8,
  OSX10_9,
  OSX10_10,
  OSX10_11,
  OSX10_12,
  OSX10_13,
  OSX10_14,
  OSX10_15,
  OSX11_0,
  Android,
  Ios
};

enum VersionComparisonOp {
  DRIVER_LESS_THAN,                    // driver <  version
  DRIVER_BUILD_ID_LESS_THAN,           // driver build id <  version
  DRIVER_LESS_THAN_OR_EQUAL,           // driver <= version
  DRIVER_BUILD_ID_LESS_THAN_OR_EQUAL,  // driver build id <= version
  DRIVER_GREATER_THAN,                 // driver >  version
  DRIVER_GREATER_THAN_OR_EQUAL,        // driver >= version
  DRIVER_EQUAL,                        // driver == version
  DRIVER_NOT_EQUAL,                    // driver != version
  DRIVER_BETWEEN_EXCLUSIVE,        // driver > version && driver < versionMax
  DRIVER_BETWEEN_INCLUSIVE,        // driver >= version && driver <= versionMax
  DRIVER_BETWEEN_INCLUSIVE_START,  // driver >= version && driver < versionMax
  DRIVER_COMPARISON_IGNORED
};

enum class DeviceFamily : uint8_t {
  All,
  IntelAll,
  NvidiaAll,
  AtiAll,
  MicrosoftAll,
  ParallelsAll,
  QualcommAll,
  AppleAll,
  AmazonAll,
  IntelGMA500,
  IntelGMA900,
  IntelGMA950,
  IntelGMA3150,
  IntelGMAX3000,
  IntelGMAX4500HD,
  IntelHDGraphicsToIvyBridge,
  IntelHDGraphicsToSandyBridge,
  IntelHaswell,
  IntelSandyBridge,
  IntelHD520,
  IntelMobileHDGraphics,
  NvidiaBlockD3D9Layers,
  RadeonX1000,
  RadeonCaicos,
  Geforce7300GT,
  Nvidia310M,
  Nvidia8800GTS,
  Bug1137716,
  Bug1116812,
  Bug1155608,
  Bug1207665,
  Bug1447141,
  AmdR600,
  NvidiaBlockWebRender,
  NvidiaRolloutWebRender,
  IntelRolloutWebRender,
  IntelModernRolloutWebRender,
  AtiRolloutWebRender,

  Max
};

enum class DeviceVendor : uint8_t {
  All,  // There is an assumption that this is the first enum
  Intel,
  NVIDIA,
  ATI,
  Microsoft,
  Parallels,
  VMWare,
  VirtualBox,
  Qualcomm,
  MicrosoftBasic,
  MicrosoftHyperV,
  Apple,
  Amazon,

  Max
};

enum DriverVendor : uint8_t {
  All,  // There is an assumption that this is the first enum
  // Wildcard for all Mesa drivers.
  MesaAll,
  // Note that the following list of Mesa drivers is not comprehensive; we pull
  // the DRI driver at runtime. These drivers are provided for convenience when
  // populating the local blocklist.
  MesaLLVMPipe,
  MesaSoftPipe,
  MesaSWRast,
  // AMD
  MesaR600,
  // Nouveau: Open-source nvidia
  MesaNouveau,
  // A generic ID to be provided when we can't determine the DRI driver on Mesa.
  MesaUnknown,
  // Wildcard for all non-Mesa drivers.
  NonMesaAll,
  // Wildcard for all hardware Mesa drivers.
  HardwareMesaAll,
  // Wildcard for all software Mesa drivers.
  SoftwareMesaAll,

  Max
};

enum class DesktopEnvironment : uint8_t {
  All,  // There is an assumption that this is the first enum
  GNOME,
  KDE,
  XFCE,
  Cinnamon,
  Enlightenment,
  LXDE,
  Openbox,
  i3,
  Mate,
  Unity,
  Pantheon,
  LXQT,
  Deepin,
  Dwm,
  Budgie,
  Unknown,
  Max
};

enum class WindowProtocol : uint8_t {
  All,  // There is an assumption that this is the first enum
  X11,
  XWayland,
  Wayland,
  WaylandDRM,
  // Wildcard for all Wayland variants, excluding XWayland.
  WaylandAll,
  // Wildcard for all X11 variants, including XWayland.
  X11All,
  Max
};

enum class BatteryStatus : uint8_t { All, Present, None };

enum class ScreenSizeStatus : uint8_t {
  All,
  Small,           // <= 1900x1200
  SmallAndMedium,  // <= 3440x1440
  Medium,          // <= 3440x1440 && > 1900x1200
  MediumAndLarge,  // >1900x1200
  Large            // > 3440x1440
};

/* Array of devices to match, or an empty array for all devices */
class GfxDeviceFamily final {
 public:
  GfxDeviceFamily() = default;

  void Append(const nsAString& aDeviceId);
  void AppendRange(int32_t aBeginDeviceId, int32_t aEndDeviceId);

  bool IsEmpty() const { return mIds.IsEmpty() && mRanges.IsEmpty(); }

  nsresult Contains(nsAString& aDeviceId) const;

 private:
  struct DeviceRange {
    int32_t mBegin;
    int32_t mEnd;
  };

  CopyableTArray mIds;
  CopyableTArray mRanges;
};

struct GfxDriverInfo {
  // If |ownDevices| is true, you are transferring ownership of the devices
  // array, and it will be deleted when this GfxDriverInfo is destroyed.
  GfxDriverInfo(OperatingSystem os, ScreenSizeStatus aScreen,
                BatteryStatus aBattery, const nsAString& desktopEnv,
                const nsAString& windowProtocol, const nsAString& vendor,
                const nsAString& driverVendor, GfxDeviceFamily* devices,
                int32_t feature, int32_t featureStatus, VersionComparisonOp op,
                uint64_t driverVersion, const char* ruleId,
                const char* suggestedVersion = nullptr, bool ownDevices = false,
                bool gpu2 = false);

  GfxDriverInfo();
  GfxDriverInfo(const GfxDriverInfo&);
  ~GfxDriverInfo();

  OperatingSystem mOperatingSystem;
  uint32_t mOperatingSystemVersion;
  ScreenSizeStatus mScreen;
  BatteryStatus mBattery;
  nsString mDesktopEnvironment;
  nsString mWindowProtocol;

  nsString mAdapterVendor;
  nsString mDriverVendor;

  const GfxDeviceFamily* mDevices;

  // Whether the mDevices array should be deleted when this structure is
  // deallocated. False by default.
  bool mDeleteDevices;

  /* A feature from nsIGfxInfo, or all features */
  int32_t mFeature;
  static int32_t allFeatures;

  /* A feature status from nsIGfxInfo */
  int32_t mFeatureStatus;

  VersionComparisonOp mComparisonOp;

  /* versions are assumed to be A.B.C.D packed as 0xAAAABBBBCCCCDDDD */
  uint64_t mDriverVersion;
  uint64_t mDriverVersionMax;
  static uint64_t allDriverVersions;

  const char* mSuggestedVersion;
  nsCString mRuleId;

  static const GfxDeviceFamily* GetDeviceFamily(DeviceFamily id);
  static GfxDeviceFamily*
      sDeviceFamilies[static_cast(DeviceFamily::Max)];

  static const nsAString& GetDesktopEnvironment(DesktopEnvironment id);
  static nsAString*
      sDesktopEnvironment[static_cast(DesktopEnvironment::Max)];

  static const nsAString& GetWindowProtocol(WindowProtocol id);
  static nsAString* sWindowProtocol[static_cast(WindowProtocol::Max)];

  static const nsAString& GetDeviceVendor(DeviceVendor id);
  static const nsAString& GetDeviceVendor(DeviceFamily id);
  static nsAString* sDeviceVendors[static_cast(DeviceVendor::Max)];

  static const nsAString& GetDriverVendor(DriverVendor id);
  static nsAString* sDriverVendors[static_cast(DriverVendor::Max)];

  nsString mModel, mHardware, mProduct, mManufacturer;

  bool mGpu2;
};

#define GFX_DRIVER_VERSION(a, b, c, d)                               \
  ((uint64_t(a) << 48) | (uint64_t(b) << 32) | (uint64_t(c) << 16) | \
   uint64_t(d))

inline uint64_t V(uint32_t a, uint32_t b, uint32_t c, uint32_t d) {
  // We make sure every driver number is padded by 0s, this will allow us the
  // easiest 'compare as if decimals' approach. See ParseDriverVersion for a
  // more extensive explanation of this approach.
  while (b > 0 && b < 1000) {
    b *= 10;
  }
  while (c > 0 && c < 1000) {
    c *= 10;
  }
  while (d > 0 && d < 1000) {
    d *= 10;
  }
  return GFX_DRIVER_VERSION(a, b, c, d);
}

// All destination string storage needs to have at least 5 bytes available.
inline bool SplitDriverVersion(const char* aSource, char* aAStr, char* aBStr,
                               char* aCStr, char* aDStr) {
  // sscanf doesn't do what we want here to we parse this manually.
  int len = strlen(aSource);

  // This "4" is hardcoded in a few places, including once as a 3.
  char* dest[4] = {aAStr, aBStr, aCStr, aDStr};
  unsigned destIdx = 0;
  unsigned destPos = 0;

  for (int i = 0; i < len; i++) {
    if (destIdx >= 4) {
      // Invalid format found. Ensure we don't access dest beyond bounds.
      return false;
    }

    if (aSource[i] == '.') {
      MOZ_ASSERT(destIdx < 4 && destPos <= 4);
      dest[destIdx++][destPos] = 0;
      destPos = 0;
      continue;
    }

    if (destPos > 3) {
      // Ignore more than 4 chars. Ensure we never access dest[destIdx]
      // beyond its bounds.
      continue;
    }

    MOZ_ASSERT(destIdx < 4 && destPos < 4);
    dest[destIdx][destPos++] = aSource[i];
  }

  // Take care of the trailing period
  if (destIdx >= 4) {
    return false;
  }

  // Add last terminator.
  MOZ_ASSERT(destIdx < 4 && destPos <= 4);
  dest[destIdx][destPos] = 0;

  if (destIdx != 3) {
    return false;
  }
  return true;
}

// This allows us to pad driver version 'substrings' with 0s, this
// effectively allows us to treat the version numbers as 'decimals'. This is
// a little strange but this method seems to do the right thing for all
// different vendor's driver strings. i.e. .98 will become 9800, which is
// larger than .978 which would become 9780.
inline void PadDriverDecimal(char* aString) {
  for (int i = 0; i < 4; i++) {
    if (!aString[i]) {
      for (int c = i; c < 4; c++) {
        aString[c] = '0';
      }
      break;
    }
  }
  aString[4] = 0;
}

inline bool ParseDriverVersion(const nsAString& aVersion,
                               uint64_t* aNumericVersion) {
  *aNumericVersion = 0;

#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
  int a, b, c, d;
  char aStr[8], bStr[8], cStr[8], dStr[8];
  /* honestly, why do I even bother */
  if (!SplitDriverVersion(NS_LossyConvertUTF16toASCII(aVersion).get(), aStr,
                          bStr, cStr, dStr))
    return false;

  PadDriverDecimal(bStr);
  PadDriverDecimal(cStr);
  PadDriverDecimal(dStr);

  a = atoi(aStr);
  b = atoi(bStr);
  c = atoi(cStr);
  d = atoi(dStr);

  if (a < 0 || a > 0xffff) return false;
  if (b < 0 || b > 0xffff) return false;
  if (c < 0 || c > 0xffff) return false;
  if (d < 0 || d > 0xffff) return false;

  *aNumericVersion = GFX_DRIVER_VERSION(a, b, c, d);
  MOZ_ASSERT(*aNumericVersion != GfxDriverInfo::allDriverVersions);
  return true;
#elif defined(ANDROID)
  // Can't use aVersion.ToInteger() because that's not compiled into our code
  // unless we have XPCOM_GLUE_AVOID_NSPR disabled.
  *aNumericVersion = atoi(NS_LossyConvertUTF16toASCII(aVersion).get());
  MOZ_ASSERT(*aNumericVersion != GfxDriverInfo::allDriverVersions);
  return true;
#else
  return false;
#endif
}

}  // namespace widget