MangoHud Standalone
Co-authored-by: jackun <jack.un@gmail.com> Co-authored-by: telans <telans@protonmail.com>pull/1/head
parent
077cac31f3
commit
dd78c02ed8
@ -0,0 +1,32 @@
|
|||||||
|
build/
|
||||||
|
__pycache__/
|
||||||
|
|
||||||
|
# Prerequisites
|
||||||
|
*.d
|
||||||
|
|
||||||
|
# Compiled Object files
|
||||||
|
*.slo
|
||||||
|
*.lo
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Compiled Dynamic libraries
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
*.dll
|
||||||
|
|
||||||
|
# Compiled Static libraries
|
||||||
|
*.lai
|
||||||
|
*.la
|
||||||
|
*.a
|
||||||
|
*.lib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
|
@ -0,0 +1,65 @@
|
|||||||
|
# MangoHud
|
||||||
|
|
||||||
|
A modification of the Mesa vulkan overlay. Personal improvements including temperature reporting and logging capabilities.
|
||||||
|
|
||||||
|
#### Comparison (outdated)
|
||||||
|
![](assets/overlay_comparison.gif)
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
- Arch linux: [PKGBUILD](https://github.com/flightlessmango/PKGBUILDS/blob/master/mangohud/PKGBUILD)
|
||||||
|
|
||||||
|
# Normal usage
|
||||||
|
|
||||||
|
To enable the MangoHud vulkan overlay layer, run :
|
||||||
|
|
||||||
|
`MANGOHUD=1 /path/to/my_vulkan_app`
|
||||||
|
|
||||||
|
Or alternatively, add `MANGOHUD=1` to your shell profile.
|
||||||
|
|
||||||
|
## MANGOHUD_CONFIG parameters
|
||||||
|
|
||||||
|
You can customize the hud by using the MANGOHUD_CONFIG environment variable while separating different options with a comma.
|
||||||
|
|
||||||
|
- `cpu_temp` : Displays current CPU temperature
|
||||||
|
- `gpu_temp` : Displays current GPU temperature
|
||||||
|
- `core_load` : Displays current CPU load per core
|
||||||
|
- `font_size` : Changes the default font size (default is 24)
|
||||||
|
- `width` : Set custom hud width
|
||||||
|
- `height` : Set custom hud height
|
||||||
|
- `position=x`: Available values for `x` include `top-left`, `top-right`, `bottom-left`, and `bottom-right`
|
||||||
|
|
||||||
|
Note: Width and Height are set automatically based on the font_size, but can be overridden.
|
||||||
|
|
||||||
|
Example: `MANGOHUD_CONFIG=cpu_temp,gpu_temp,position=top-right,height=500,font_size=32`
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
- `MANGOHUD_OUTPUT` : Define name and location of the output file (Required for logging)
|
||||||
|
- `MANGOHUD_FONT`: Change default font (set location to .TTF/.OTF file )
|
||||||
|
|
||||||
|
## Keybindings
|
||||||
|
- `F2` : Toggle Logging
|
||||||
|
- `F12`: Toggle Hud
|
||||||
|
|
||||||
|
## MangoHud fps logging
|
||||||
|
|
||||||
|
When you toggle logging (using the keybind `F2`), a file is created with your chosen name (using `MANGOHUD_OUTPUT`) plus a date & timestamp.
|
||||||
|
|
||||||
|
This file can be uploaded to [Flightlessmango.com](https://flightlessmango.com/games/user_benchmarks) to create graphs automatically.
|
||||||
|
you can share the created page with others, just link it.
|
||||||
|
|
||||||
|
#### Multiple log files
|
||||||
|
|
||||||
|
It's possible to upload multiple files when using [Flightlessmango.com](https://flightlessmango.com/games/user_benchmarks). You can rename them to your preferred names and upload them in a batch.
|
||||||
|
These filenames will be used as the legend in the graph.
|
||||||
|
|
||||||
|
#### Log uploading walkthrough
|
||||||
|
|
||||||
|
![](assets/log_upload_example.gif)
|
||||||
|
|
||||||
|
# Notable changes
|
||||||
|
- Removed hud decoration [90a2212](https://github.com/flightlessmango/mesa/commit/90a2212055a8047d46d0220d5fdc30a76900aaed)
|
||||||
|
- Changed frametime graph to Lines instead of Histogram [e40533b](https://github.com/flightlessmango/mesa/commit/e40533b7f46858e5b9f08829e789277b2364d5d1)
|
||||||
|
- Set static min/max ms on frametime graph to act like Afterburners graph [df5238f](https://github.com/flightlessmango/mesa/commit/df5238f990218f5d6e698d572b05ddd19e52b108)
|
||||||
|
- Added CPU/GPU usage (Only Nvidia and AMD)
|
||||||
|
- Changed font to UbuntuMono-Bold [73f0aa9](https://github.com/flightlessmango/mesa/commit/73f0aa94d382365205a4a4128d82208315b0b190)
|
||||||
|
- Increased hud font size [b7d238b](https://github.com/flightlessmango/mesa/commit/b7d238b07eb82153f272d34bf7d1353b701f32e0)
|
Binary file not shown.
After Width: | Height: | Size: 4.7 MiB |
Binary file not shown.
After Width: | Height: | Size: 8.4 MiB |
@ -0,0 +1,447 @@
|
|||||||
|
# encoding=utf-8
|
||||||
|
# Copyright © 2017 Intel Corporation
|
||||||
|
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
|
||||||
|
"""Create enum to string functions for vulkan using vk.xml."""
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import textwrap
|
||||||
|
import xml.etree.cElementTree as et
|
||||||
|
|
||||||
|
from mako.template import Template
|
||||||
|
|
||||||
|
COPYRIGHT = textwrap.dedent(u"""\
|
||||||
|
* Copyright © 2017 Intel Corporation
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.""")
|
||||||
|
|
||||||
|
C_TEMPLATE = Template(textwrap.dedent(u"""\
|
||||||
|
/* Autogenerated file -- do not edit
|
||||||
|
* generated by ${file}
|
||||||
|
*
|
||||||
|
${copyright}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
#include <vulkan/vk_android_native_buffer.h>
|
||||||
|
#include "../src/mesa/util/macros.h"
|
||||||
|
#include "vk_enum_to_str.h"
|
||||||
|
|
||||||
|
% for enum in enums:
|
||||||
|
|
||||||
|
% if enum.guard:
|
||||||
|
#ifdef ${enum.guard}
|
||||||
|
% endif
|
||||||
|
const char *
|
||||||
|
vk_${enum.name[2:]}_to_str(${enum.name} input)
|
||||||
|
{
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wswitch"
|
||||||
|
switch(input) {
|
||||||
|
% for v in sorted(enum.values.keys()):
|
||||||
|
case ${v}:
|
||||||
|
return "${enum.values[v]}";
|
||||||
|
% endfor
|
||||||
|
}
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
unreachable("Undefined enum value.");
|
||||||
|
}
|
||||||
|
|
||||||
|
% if enum.guard:
|
||||||
|
#endif
|
||||||
|
% endif
|
||||||
|
%endfor
|
||||||
|
|
||||||
|
size_t vk_structure_type_size(const struct VkBaseInStructure *item)
|
||||||
|
{
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wswitch"
|
||||||
|
switch(item->sType) {
|
||||||
|
% for struct in structs:
|
||||||
|
% if struct.extension is not None and struct.extension.define is not None:
|
||||||
|
#ifdef ${struct.extension.define}
|
||||||
|
case ${struct.stype}: return sizeof(${struct.name});
|
||||||
|
#endif
|
||||||
|
% else:
|
||||||
|
case ${struct.stype}: return sizeof(${struct.name});
|
||||||
|
% endif
|
||||||
|
%endfor
|
||||||
|
}
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
unreachable("Undefined struct type.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void vk_load_instance_commands(VkInstance instance,
|
||||||
|
PFN_vkGetInstanceProcAddr gpa,
|
||||||
|
struct vk_instance_dispatch_table *table)
|
||||||
|
{
|
||||||
|
memset(table, 0, sizeof(*table));
|
||||||
|
table->GetInstanceProcAddr = gpa;
|
||||||
|
% for cmd in commands:
|
||||||
|
% if not cmd.device_entrypoint and cmd.name != 'vkGetInstanceProcAddr':
|
||||||
|
% if cmd.extension is not None and cmd.extension.define is not None:
|
||||||
|
#ifdef ${cmd.extension.define}
|
||||||
|
table->${cmd.name[2:]} = (PFN_${cmd.name}) gpa(instance, "${cmd.name}");
|
||||||
|
#endif
|
||||||
|
% else:
|
||||||
|
table->${cmd.name[2:]} = (PFN_${cmd.name}) gpa(instance, "${cmd.name}");
|
||||||
|
% endif
|
||||||
|
% endif
|
||||||
|
%endfor
|
||||||
|
}
|
||||||
|
|
||||||
|
void vk_load_device_commands(VkDevice device,
|
||||||
|
PFN_vkGetDeviceProcAddr gpa,
|
||||||
|
struct vk_device_dispatch_table *table)
|
||||||
|
{
|
||||||
|
memset(table, 0, sizeof(*table));
|
||||||
|
table->GetDeviceProcAddr = gpa;
|
||||||
|
% for cmd in commands:
|
||||||
|
% if cmd.device_entrypoint and cmd.name != 'vkGetDeviceProcAddr':
|
||||||
|
% if cmd.extension is not None and cmd.extension.define is not None:
|
||||||
|
#ifdef ${cmd.extension.define}
|
||||||
|
table->${cmd.name[2:]} = (PFN_${cmd.name}) gpa(device, "${cmd.name}");
|
||||||
|
#endif
|
||||||
|
% else:
|
||||||
|
table->${cmd.name[2:]} = (PFN_${cmd.name}) gpa(device, "${cmd.name}");
|
||||||
|
% endif
|
||||||
|
% endif
|
||||||
|
%endfor
|
||||||
|
}
|
||||||
|
"""),
|
||||||
|
output_encoding='utf-8')
|
||||||
|
|
||||||
|
H_TEMPLATE = Template(textwrap.dedent(u"""\
|
||||||
|
/* Autogenerated file -- do not edit
|
||||||
|
* generated by ${file}
|
||||||
|
*
|
||||||
|
${copyright}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MESA_VK_ENUM_TO_STR_H
|
||||||
|
#define MESA_VK_ENUM_TO_STR_H
|
||||||
|
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
#include <vulkan/vk_android_native_buffer.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
% for ext in extensions:
|
||||||
|
#define _${ext.name}_number (${ext.number})
|
||||||
|
% endfor
|
||||||
|
|
||||||
|
% for enum in enums:
|
||||||
|
% if enum.guard:
|
||||||
|
#ifdef ${enum.guard}
|
||||||
|
% endif
|
||||||
|
const char * vk_${enum.name[2:]}_to_str(${enum.name} input);
|
||||||
|
% if enum.guard:
|
||||||
|
#endif
|
||||||
|
% endif
|
||||||
|
% endfor
|
||||||
|
|
||||||
|
size_t vk_structure_type_size(const struct VkBaseInStructure *item);
|
||||||
|
|
||||||
|
struct vk_instance_dispatch_table {
|
||||||
|
PFN_vkGetInstanceProcAddr GetInstanceProcAddr;
|
||||||
|
% for cmd in commands:
|
||||||
|
% if not cmd.device_entrypoint and cmd.name != 'vkGetInstanceProcAddr':
|
||||||
|
% if cmd.extension is not None and cmd.extension.define is not None:
|
||||||
|
#ifdef ${cmd.extension.define}
|
||||||
|
PFN_${cmd.name} ${cmd.name[2:]};
|
||||||
|
#endif
|
||||||
|
% else:
|
||||||
|
PFN_${cmd.name} ${cmd.name[2:]};
|
||||||
|
% endif
|
||||||
|
% endif
|
||||||
|
%endfor
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vk_device_dispatch_table {
|
||||||
|
PFN_vkGetDeviceProcAddr GetDeviceProcAddr;
|
||||||
|
% for cmd in commands:
|
||||||
|
% if cmd.device_entrypoint and cmd.name != 'vkGetDeviceProcAddr':
|
||||||
|
% if cmd.extension is not None and cmd.extension.define is not None:
|
||||||
|
#ifdef ${cmd.extension.define}
|
||||||
|
PFN_${cmd.name} ${cmd.name[2:]};
|
||||||
|
#endif
|
||||||
|
% else:
|
||||||
|
PFN_${cmd.name} ${cmd.name[2:]};
|
||||||
|
% endif
|
||||||
|
% endif
|
||||||
|
%endfor
|
||||||
|
};
|
||||||
|
|
||||||
|
void vk_load_instance_commands(VkInstance instance, PFN_vkGetInstanceProcAddr gpa, struct vk_instance_dispatch_table *table);
|
||||||
|
void vk_load_device_commands(VkDevice device, PFN_vkGetDeviceProcAddr gpa, struct vk_device_dispatch_table *table);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif"""),
|
||||||
|
output_encoding='utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
class NamedFactory(object):
|
||||||
|
"""Factory for creating enums."""
|
||||||
|
|
||||||
|
def __init__(self, type_):
|
||||||
|
self.registry = {}
|
||||||
|
self.type = type_
|
||||||
|
|
||||||
|
def __call__(self, name, **kwargs):
|
||||||
|
try:
|
||||||
|
return self.registry[name]
|
||||||
|
except KeyError:
|
||||||
|
n = self.registry[name] = self.type(name, **kwargs)
|
||||||
|
return n
|
||||||
|
|
||||||
|
def get(self, name):
|
||||||
|
return self.registry.get(name)
|
||||||
|
|
||||||
|
|
||||||
|
class VkExtension(object):
|
||||||
|
"""Simple struct-like class representing extensions"""
|
||||||
|
|
||||||
|
def __init__(self, name, number=None, define=None):
|
||||||
|
self.name = name
|
||||||
|
self.number = number
|
||||||
|
self.define = define
|
||||||
|
|
||||||
|
|
||||||
|
class VkEnum(object):
|
||||||
|
"""Simple struct-like class representing a single Vulkan Enum."""
|
||||||
|
|
||||||
|
def __init__(self, name, values=None):
|
||||||
|
self.name = name
|
||||||
|
self.extension = None
|
||||||
|
# Maps numbers to names
|
||||||
|
self.values = values or dict()
|
||||||
|
self.name_to_value = dict()
|
||||||
|
self.guard = None
|
||||||
|
self.name_to_alias_list = {}
|
||||||
|
|
||||||
|
def add_value(self, name, value=None,
|
||||||
|
extnum=None, offset=None, alias=None,
|
||||||
|
error=False):
|
||||||
|
if alias is not None:
|
||||||
|
assert value is None and offset is None
|
||||||
|
if alias not in self.name_to_value:
|
||||||
|
# We don't have this alias yet. Just record the alias and
|
||||||
|
# we'll deal with it later.
|
||||||
|
alias_list = self.name_to_alias_list.get(alias, [])
|
||||||
|
alias_list.append(name);
|
||||||
|
return
|
||||||
|
|
||||||
|
# Use the value from the alias
|
||||||
|
value = self.name_to_value[alias]
|
||||||
|
|
||||||
|
assert value is not None or extnum is not None
|
||||||
|
if value is None:
|
||||||
|
value = 1000000000 + (extnum - 1) * 1000 + offset
|
||||||
|
if error:
|
||||||
|
value = -value
|
||||||
|
|
||||||
|
self.name_to_value[name] = value
|
||||||
|
if value not in self.values:
|
||||||
|
self.values[value] = name
|
||||||
|
elif len(self.values[value]) > len(name):
|
||||||
|
self.values[value] = name
|
||||||
|
|
||||||
|
# Now that the value has been fully added, resolve aliases, if any.
|
||||||
|
if name in self.name_to_alias_list:
|
||||||
|
for alias in self.name_to_alias_list[name]:
|
||||||
|
add_value(alias, value)
|
||||||
|
del self.name_to_alias_list[name]
|
||||||
|
|
||||||
|
def add_value_from_xml(self, elem, extension=None):
|
||||||
|
self.extension = extension
|
||||||
|
if 'value' in elem.attrib:
|
||||||
|
self.add_value(elem.attrib['name'],
|
||||||
|
value=int(elem.attrib['value'], base=0))
|
||||||
|
elif 'alias' in elem.attrib:
|
||||||
|
self.add_value(elem.attrib['name'], alias=elem.attrib['alias'])
|
||||||
|
else:
|
||||||
|
error = 'dir' in elem.attrib and elem.attrib['dir'] == '-'
|
||||||
|
if 'extnumber' in elem.attrib:
|
||||||
|
extnum = int(elem.attrib['extnumber'])
|
||||||
|
else:
|
||||||
|
extnum = extension.number
|
||||||
|
self.add_value(elem.attrib['name'],
|
||||||
|
extnum=extnum,
|
||||||
|
offset=int(elem.attrib['offset']),
|
||||||
|
error=error)
|
||||||
|
|
||||||
|
def set_guard(self, g):
|
||||||
|
self.guard = g
|
||||||
|
|
||||||
|
|
||||||
|
class VkCommand(object):
|
||||||
|
"""Simple struct-like class representing a single Vulkan command"""
|
||||||
|
|
||||||
|
def __init__(self, name, device_entrypoint=False):
|
||||||
|
self.name = name
|
||||||
|
self.device_entrypoint = device_entrypoint
|
||||||
|
self.extension = None
|
||||||
|
|
||||||
|
|
||||||
|
class VkChainStruct(object):
|
||||||
|
"""Simple struct-like class representing a single Vulkan struct identified with a VkStructureType"""
|
||||||
|
def __init__(self, name, stype):
|
||||||
|
self.name = name
|
||||||
|
self.stype = stype
|
||||||
|
self.extension = None
|
||||||
|
|
||||||
|
|
||||||
|
def struct_get_stype(xml_node):
|
||||||
|
for member in xml_node.findall('./member'):
|
||||||
|
name = member.findall('./name')
|
||||||
|
if len(name) > 0 and name[0].text == "sType":
|
||||||
|
return member.get('values')
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def parse_xml(cmd_factory, enum_factory, ext_factory, struct_factory, filename):
|
||||||
|
"""Parse the XML file. Accumulate results into the factories.
|
||||||
|
|
||||||
|
This parser is a memory efficient iterative XML parser that returns a list
|
||||||
|
of VkEnum objects.
|
||||||
|
"""
|
||||||
|
|
||||||
|
xml = et.parse(filename)
|
||||||
|
|
||||||
|
for enum_type in xml.findall('./enums[@type="enum"]'):
|
||||||
|
enum = enum_factory(enum_type.attrib['name'])
|
||||||
|
for value in enum_type.findall('./enum'):
|
||||||
|
enum.add_value_from_xml(value)
|
||||||
|
|
||||||
|
for value in xml.findall('./feature/require/enum[@extends]'):
|
||||||
|
enum = enum_factory.get(value.attrib['extends'])
|
||||||
|
if enum is not None:
|
||||||
|
enum.add_value_from_xml(value)
|
||||||
|
|
||||||
|
for command in xml.findall('./commands/command'):
|
||||||
|
name = command.find('./proto/name')
|
||||||
|
first_arg = command.find('./param/type')
|
||||||
|
# Some commands are alias KHR -> nonKHR, ignore those
|
||||||
|
if name is not None:
|
||||||
|
cmd_factory(name.text,
|
||||||
|
device_entrypoint=(first_arg.text in ('VkDevice', 'VkCommandBuffer', 'VkQueue')))
|
||||||
|
|
||||||
|
for struct_type in xml.findall('./types/type[@category="struct"]'):
|
||||||
|
name = struct_type.attrib['name']
|
||||||
|
stype = struct_get_stype(struct_type)
|
||||||
|
if stype is not None:
|
||||||
|
struct_factory(name, stype=stype)
|
||||||
|
|
||||||
|
platform_define = {}
|
||||||
|
for platform in xml.findall('./platforms/platform'):
|
||||||
|
name = platform.attrib['name']
|
||||||
|
define = platform.attrib['protect']
|
||||||
|
platform_define[name] = define
|
||||||
|
|
||||||
|
for ext_elem in xml.findall('./extensions/extension[@supported="vulkan"]'):
|
||||||
|
define = None
|
||||||
|
if "platform" in ext_elem.attrib:
|
||||||
|
define = platform_define[ext_elem.attrib['platform']]
|
||||||
|
extension = ext_factory(ext_elem.attrib['name'],
|
||||||
|
number=int(ext_elem.attrib['number']),
|
||||||
|
define=define)
|
||||||
|
|
||||||
|
for value in ext_elem.findall('./require/enum[@extends]'):
|
||||||
|
enum = enum_factory.get(value.attrib['extends'])
|
||||||
|
if enum is not None:
|
||||||
|
enum.add_value_from_xml(value, extension)
|
||||||
|
for t in ext_elem.findall('./require/type'):
|
||||||
|
struct = struct_factory.get(t.attrib['name'])
|
||||||
|
if struct is not None:
|
||||||
|
struct.extension = extension
|
||||||
|
|
||||||
|
if define:
|
||||||
|
for value in ext_elem.findall('./require/type[@name]'):
|
||||||
|
enum = enum_factory.get(value.attrib['name'])
|
||||||
|
if enum is not None:
|
||||||
|
enum.set_guard(define)
|
||||||
|
|
||||||
|
for t in ext_elem.findall('./require/command'):
|
||||||
|
command = cmd_factory.get(t.attrib['name'])
|
||||||
|
if command is not None:
|
||||||
|
command.extension = extension
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('--xml', required=True,
|
||||||
|
help='Vulkan API XML files',
|
||||||
|
action='append',
|
||||||
|
dest='xml_files')
|
||||||
|
parser.add_argument('--outdir',
|
||||||
|
help='Directory to put the generated files in',
|
||||||
|
required=True)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
command_factory = NamedFactory(VkCommand)
|
||||||
|
enum_factory = NamedFactory(VkEnum)
|
||||||
|
ext_factory = NamedFactory(VkExtension)
|
||||||
|
struct_factory = NamedFactory(VkChainStruct)
|
||||||
|
for filename in args.xml_files:
|
||||||
|
parse_xml(command_factory, enum_factory, ext_factory, struct_factory, filename)
|
||||||
|
commands = sorted(command_factory.registry.values(), key=lambda e: e.name)
|
||||||
|
enums = sorted(enum_factory.registry.values(), key=lambda e: e.name)
|
||||||
|
extensions = sorted(ext_factory.registry.values(), key=lambda e: e.name)
|
||||||
|
structs = sorted(struct_factory.registry.values(), key=lambda e: e.name)
|
||||||
|
|
||||||
|
for template, file_ in [(C_TEMPLATE, os.path.join(args.outdir, 'vk_enum_to_str.c')),
|
||||||
|
(H_TEMPLATE, os.path.join(args.outdir, 'vk_enum_to_str.h'))]:
|
||||||
|
with open(file_, 'wb') as f:
|
||||||
|
f.write(template.render(
|
||||||
|
file=os.path.basename(__file__),
|
||||||
|
commands=commands,
|
||||||
|
enums=enums,
|
||||||
|
extensions=extensions,
|
||||||
|
structs=structs,
|
||||||
|
copyright=COPYRIGHT))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
@ -0,0 +1,50 @@
|
|||||||
|
"""
|
||||||
|
Generate the contents of the git_sha1.h file.
|
||||||
|
The output of this script goes to stdout.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def get_git_sha1():
|
||||||
|
"""Try to get the git SHA1 with git rev-parse."""
|
||||||
|
git_dir = os.path.join(os.path.dirname(sys.argv[0]), '..', '.git')
|
||||||
|
try:
|
||||||
|
git_sha1 = subprocess.check_output([
|
||||||
|
'git',
|
||||||
|
'--git-dir=' + git_dir,
|
||||||
|
'rev-parse',
|
||||||
|
'HEAD',
|
||||||
|
], stderr=open(os.devnull, 'w')).decode("ascii")
|
||||||
|
except:
|
||||||
|
# don't print anything if it fails
|
||||||
|
git_sha1 = ''
|
||||||
|
return git_sha1
|
||||||
|
|
||||||
|
def write_if_different(contents):
|
||||||
|
"""
|
||||||
|
Avoid touching the output file if it doesn't need modifications
|
||||||
|
Useful to avoid triggering rebuilds when nothing has changed.
|
||||||
|
"""
|
||||||
|
if os.path.isfile(args.output):
|
||||||
|
with open(args.output, 'r') as file:
|
||||||
|
if file.read() == contents:
|
||||||
|
return
|
||||||
|
with open(args.output, 'w') as file:
|
||||||
|
file.write(contents)
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('--output', help='File to write the #define in',
|
||||||
|
required=True)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
git_sha1 = os.environ.get('MESA_GIT_SHA1_OVERRIDE', get_git_sha1())[:10]
|
||||||
|
if git_sha1:
|
||||||
|
write_if_different('#define MESA_GIT_SHA1 " (git-' + git_sha1 + ')"')
|
||||||
|
else:
|
||||||
|
write_if_different('#define MESA_GIT_SHA1 ""')
|
@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
#ifndef __VK_ANDROID_NATIVE_BUFFER_H__
|
||||||
|
#define __VK_ANDROID_NATIVE_BUFFER_H__
|
||||||
|
|
||||||
|
/* MESA: A hack to avoid #ifdefs in driver code. */
|
||||||
|
#ifdef ANDROID
|
||||||
|
#include <system/window.h>
|
||||||
|
#include <cutils/native_handle.h>
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
#else
|
||||||
|
typedef void *buffer_handle_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define VK_ANDROID_native_buffer 1
|
||||||
|
#define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NUMBER 11
|
||||||
|
|
||||||
|
/* NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 6
|
||||||
|
*
|
||||||
|
* This version of the extension transitions from gralloc0 to gralloc1 usage
|
||||||
|
* flags (int -> 2x uint64_t). The WSI implementation will temporarily continue
|
||||||
|
* to fill out deprecated fields in VkNativeBufferANDROID, and will call the
|
||||||
|
* deprecated vkGetSwapchainGrallocUsageANDROID if the new
|
||||||
|
* vkGetSwapchainGrallocUsage2ANDROID is not supported. This transitionary
|
||||||
|
* backwards-compatibility support is temporary, and will likely be removed in
|
||||||
|
* (along with all gralloc0 support) in a future release.
|
||||||
|
*/
|
||||||
|
#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 7
|
||||||
|
#define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME "VK_ANDROID_native_buffer"
|
||||||
|
|
||||||
|
#define VK_ANDROID_NATIVE_BUFFER_ENUM(type,id) ((type)(1000000000 + (1000 * (VK_ANDROID_NATIVE_BUFFER_EXTENSION_NUMBER - 1)) + (id)))
|
||||||
|
#define VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 0)
|
||||||
|
#define VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 1)
|
||||||
|
#define VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 2)
|
||||||
|
|
||||||
|
typedef enum VkSwapchainImageUsageFlagBitsANDROID {
|
||||||
|
VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID = 0x00000001,
|
||||||
|
VK_SWAPCHAIN_IMAGE_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} VkSwapchainImageUsageFlagBitsANDROID;
|
||||||
|
typedef VkFlags VkSwapchainImageUsageFlagsANDROID;
|
||||||
|
typedef struct {
|
||||||
|
VkStructureType sType; // must be VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID
|
||||||
|
const void* pNext;
|
||||||
|
|
||||||
|
// Buffer handle and stride returned from gralloc alloc()
|
||||||
|
buffer_handle_t handle;
|
||||||
|
int stride;
|
||||||
|
|
||||||
|
// Gralloc format and usage requested when the buffer was allocated.
|
||||||
|
int format;
|
||||||
|
int usage; // DEPRECATED in SPEC_VERSION 6
|
||||||
|
|
||||||
|
// -- Added in SPEC_VERSION 6 --
|
||||||
|
struct {
|
||||||
|
uint64_t consumer;
|
||||||
|
uint64_t producer;
|
||||||
|
} usage2;
|
||||||
|
} VkNativeBufferANDROID;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
VkStructureType sType; // must be VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID
|
||||||
|
const void* pNext;
|
||||||
|
|
||||||
|
VkSwapchainImageUsageFlagsANDROID usage;
|
||||||
|
} VkSwapchainImageCreateInfoANDROID;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
VkStructureType sType; // must be VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID
|
||||||
|
const void* pNext;
|
||||||
|
|
||||||
|
VkBool32 sharedImage;
|
||||||
|
} VkPhysicalDevicePresentationPropertiesANDROID;
|
||||||
|
|
||||||
|
// -- DEPRECATED in SPEC_VERSION 6 --
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsageANDROID)(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage);
|
||||||
|
// -- ADDED in SPEC_VERSION 6 --
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage2ANDROID)(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage);
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkAcquireImageANDROID)(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkQueueSignalReleaseImageANDROID)(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
// -- DEPRECATED in SPEC_VERSION 6 --
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsageANDROID(
|
||||||
|
VkDevice device,
|
||||||
|
VkFormat format,
|
||||||
|
VkImageUsageFlags imageUsage,
|
||||||
|
int* grallocUsage
|
||||||
|
);
|
||||||
|
// -- ADDED in SPEC_VERSION 6 --
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage2ANDROID(
|
||||||
|
VkDevice device,
|
||||||
|
VkFormat format,
|
||||||
|
VkImageUsageFlags imageUsage,
|
||||||
|
VkSwapchainImageUsageFlagsANDROID swapchainImageUsage,
|
||||||
|
uint64_t* grallocConsumerUsage,
|
||||||
|
uint64_t* grallocProducerUsage
|
||||||
|
);
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkAcquireImageANDROID(
|
||||||
|
VkDevice device,
|
||||||
|
VkImage image,
|
||||||
|
int nativeFenceFd,
|
||||||
|
VkSemaphore semaphore,
|
||||||
|
VkFence fence
|
||||||
|
);
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkQueueSignalReleaseImageANDROID(
|
||||||
|
VkQueue queue,
|
||||||
|
uint32_t waitSemaphoreCount,
|
||||||
|
const VkSemaphore* pWaitSemaphores,
|
||||||
|
VkImage image,
|
||||||
|
int* pNativeFenceFd
|
||||||
|
);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // __VK_ANDROID_NATIVE_BUFFER_H__
|
@ -0,0 +1,225 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2017 Intel Corporation
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice (including the next
|
||||||
|
* paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
* Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
* IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef VK_UTIL_H
|
||||||
|
#define VK_UTIL_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* common inlines and macros for vulkan drivers */
|
||||||
|
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
|
||||||
|
#define vk_foreach_struct(__iter, __start) \
|
||||||
|
for (struct VkBaseOutStructure *__iter = (struct VkBaseOutStructure *)(__start); \
|
||||||
|
__iter; __iter = __iter->pNext)
|
||||||
|
|
||||||
|
#define vk_foreach_struct_const(__iter, __start) \
|
||||||
|
for (const struct VkBaseInStructure *__iter = (const struct VkBaseInStructure *)(__start); \
|
||||||
|
__iter; __iter = __iter->pNext)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper for a Vulkan output array. A Vulkan output array is one that
|
||||||
|
* follows the convention of the parameters to
|
||||||
|
* vkGetPhysicalDeviceQueueFamilyProperties().
|
||||||
|
*
|
||||||
|
* Example Usage:
|
||||||
|
*
|
||||||
|
* VkResult
|
||||||
|
* vkGetPhysicalDeviceQueueFamilyProperties(
|
||||||
|
* VkPhysicalDevice physicalDevice,
|
||||||
|
* uint32_t* pQueueFamilyPropertyCount,
|
||||||
|
* VkQueueFamilyProperties* pQueueFamilyProperties)
|
||||||
|
* {
|
||||||
|
* VK_OUTARRAY_MAKE(props, pQueueFamilyProperties,
|
||||||
|
* pQueueFamilyPropertyCount);
|
||||||
|
*
|
||||||
|
* vk_outarray_append(&props, p) {
|
||||||
|
* p->queueFlags = ...;
|
||||||
|
* p->queueCount = ...;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* vk_outarray_append(&props, p) {
|
||||||
|
* p->queueFlags = ...;
|
||||||
|
* p->queueCount = ...;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* return vk_outarray_status(&props);
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
struct __vk_outarray {
|
||||||
|
/** May be null. */
|
||||||
|
void *data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Capacity, in number of elements. Capacity is unlimited (UINT32_MAX) if
|
||||||
|
* data is null.
|
||||||
|
*/
|
||||||
|
uint32_t cap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count of elements successfully written to the array. Every write is
|
||||||
|
* considered successful if data is null.
|
||||||
|
*/
|
||||||
|
uint32_t *filled_len;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count of elements that would have been written to the array if its
|
||||||
|
* capacity were sufficient. Vulkan functions often return VK_INCOMPLETE
|
||||||
|
* when `*filled_len < wanted_len`.
|
||||||
|
*/
|
||||||
|
uint32_t wanted_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
__vk_outarray_init(struct __vk_outarray *a,
|
||||||
|
void *data, uint32_t *restrict len)
|
||||||
|
{
|
||||||
|
a->data = data;
|
||||||
|
a->cap = *len;
|
||||||
|
a->filled_len = len;
|
||||||
|
*a->filled_len = 0;
|
||||||
|
a->wanted_len = 0;
|
||||||
|
|
||||||
|
if (a->data == NULL)
|
||||||
|
a->cap = UINT32_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline VkResult
|
||||||
|
__vk_outarray_status(const struct __vk_outarray *a)
|
||||||
|
{
|
||||||
|
if (*a->filled_len < a->wanted_len)
|
||||||
|
return VK_INCOMPLETE;
|
||||||
|
else
|
||||||
|
return VK_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void *
|
||||||
|
__vk_outarray_next(struct __vk_outarray *a, size_t elem_size)
|
||||||
|
{
|
||||||
|
void *p = NULL;
|
||||||
|
|
||||||
|
a->wanted_len += 1;
|
||||||
|
|
||||||
|
if (*a->filled_len >= a->cap)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (a->data != NULL)
|
||||||
|
p = (uint8_t *)a->data + (*a->filled_len) * elem_size;
|
||||||
|
|
||||||
|
*a->filled_len += 1;
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define vk_outarray(elem_t) \
|
||||||
|
struct { \
|
||||||
|
struct __vk_outarray base; \
|
||||||
|
elem_t meta[]; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define vk_outarray_typeof_elem(a) __typeof__((a)->meta[0])
|
||||||
|
#define vk_outarray_sizeof_elem(a) sizeof((a)->meta[0])
|
||||||
|
|
||||||
|
#define vk_outarray_init(a, data, len) \
|
||||||
|
__vk_outarray_init(&(a)->base, (data), (len))
|
||||||
|
|
||||||
|
#define VK_OUTARRAY_MAKE(name, data, len) \
|
||||||
|
vk_outarray(__typeof__((data)[0])) name; \
|
||||||
|
vk_outarray_init(&name, (data), (len))
|
||||||
|
|
||||||
|
#define vk_outarray_status(a) \
|
||||||
|
__vk_outarray_status(&(a)->base)
|
||||||
|
|
||||||
|
#define vk_outarray_next(a) \
|
||||||
|
((vk_outarray_typeof_elem(a) *) \
|
||||||
|
__vk_outarray_next(&(a)->base, vk_outarray_sizeof_elem(a)))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append to a Vulkan output array.
|
||||||
|
*
|
||||||
|
* This is a block-based macro. For example:
|
||||||
|
*
|
||||||
|
* vk_outarray_append(&a, elem) {
|
||||||
|
* elem->foo = ...;
|
||||||
|
* elem->bar = ...;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* The array `a` has type `vk_outarray(elem_t) *`. It is usually declared with
|
||||||
|
* VK_OUTARRAY_MAKE(). The variable `elem` is block-scoped and has type
|
||||||
|
* `elem_t *`.
|
||||||
|
*
|
||||||
|
* The macro unconditionally increments the array's `wanted_len`. If the array
|
||||||
|
* is not full, then the macro also increment its `filled_len` and then
|
||||||
|
* executes the block. When the block is executed, `elem` is non-null and
|
||||||
|
* points to the newly appended element.
|
||||||
|
*/
|
||||||
|
#define vk_outarray_append(a, elem) \
|
||||||
|
for (vk_outarray_typeof_elem(a) *elem = vk_outarray_next(a); \
|
||||||
|
elem != NULL; elem = NULL)
|
||||||
|
|
||||||
|
static inline void *
|
||||||
|
__vk_find_struct(void *start, VkStructureType sType)
|
||||||
|
{
|
||||||
|
vk_foreach_struct(s, start) {
|
||||||
|
if (s->sType == sType)
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define vk_find_struct(__start, __sType) \
|
||||||
|
__vk_find_struct((__start), VK_STRUCTURE_TYPE_##__sType)
|
||||||
|
|
||||||
|
#define vk_find_struct_const(__start, __sType) \
|
||||||
|
(const void *)__vk_find_struct((void *)(__start), VK_STRUCTURE_TYPE_##__sType)
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
__vk_append_struct(void *start, void *element)
|
||||||
|
{
|
||||||
|
vk_foreach_struct(s, start) {
|
||||||
|
if (s->pNext)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
s->pNext = (struct VkBaseOutStructure *) element;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t vk_get_driver_version(void);
|
||||||
|
|
||||||
|
uint32_t vk_get_version_override(void);
|
||||||
|
|
||||||
|
#define VK_EXT_OFFSET (1000000000UL)
|
||||||
|
#define VK_ENUM_EXTENSION(__enum) \
|
||||||
|
((__enum) >= VK_EXT_OFFSET ? ((((__enum) - VK_EXT_OFFSET) / 1000UL) + 1) : 0)
|
||||||
|
#define VK_ENUM_OFFSET(__enum) \
|
||||||
|
((__enum) >= VK_EXT_OFFSET ? ((__enum) % 1000) : (__enum))
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* VK_UTIL_H */
|
@ -0,0 +1,260 @@
|
|||||||
|
# Copyright © 2019 Intel Corporation
|
||||||
|
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
|
||||||
|
project('mangohud',
|
||||||
|
['c', 'cpp'],
|
||||||
|
version : 'v1.0.0',
|
||||||
|
license : 'MIT',
|
||||||
|
meson_version : '>= 0.46',
|
||||||
|
default_options : ['buildtype=release', 'b_ndebug=if-release', 'c_std=c99', 'cpp_std=c++14']
|
||||||
|
)
|
||||||
|
|
||||||
|
cc = meson.get_compiler('c')
|
||||||
|
cpp = meson.get_compiler('cpp')
|
||||||
|
prog_python = import('python').find_installation('python3')
|
||||||
|
|
||||||
|
pre_args = [
|
||||||
|
'-D__STDC_CONSTANT_MACROS',
|
||||||
|
'-D__STDC_FORMAT_MACROS',
|
||||||
|
'-D__STDC_LIMIT_MACROS',
|
||||||
|
'-DPACKAGE_VERSION="@0@"'.format(meson.project_version()),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Define DEBUG for debug builds only (debugoptimized is not included on this one)
|
||||||
|
if get_option('buildtype') == 'debug'
|
||||||
|
pre_args += '-DDEBUG'
|
||||||
|
endif
|
||||||
|
|
||||||
|
# TODO: this is very incomplete
|
||||||
|
if ['linux', 'cygwin', 'gnu'].contains(host_machine.system())
|
||||||
|
pre_args += '-D_GNU_SOURCE'
|
||||||
|
pre_args += '-DHAVE_PTHREAD'
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Check for GCC style atomics
|
||||||
|
if cc.compiles('''#include <stdint.h>
|
||||||
|
int main() {
|
||||||
|
struct {
|
||||||
|
uint64_t *v;
|
||||||
|
} x;
|
||||||
|
return (int)__atomic_load_n(x.v, __ATOMIC_ACQUIRE) &
|
||||||
|
(int)__atomic_add_fetch(x.v, (uint64_t)1, __ATOMIC_ACQ_REL);
|
||||||
|
|
||||||
|
}''',
|
||||||
|
name : 'GCC atomic builtins')
|
||||||
|
pre_args += '-DUSE_GCC_ATOMIC_BUILTINS'
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Not in C99, needs POSIX
|
||||||
|
if cc.compiles('''
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <time.h>
|
||||||
|
int main() {
|
||||||
|
struct timespec ts;
|
||||||
|
return timespec_get(&ts, TIME_UTC);
|
||||||
|
|
||||||
|
}''',
|
||||||
|
name : 'Supports timespec_get')
|
||||||
|
pre_args += '-DHAVE_TIMESPEC_GET'
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Check for GCC style builtins
|
||||||
|
foreach b : ['bswap32', 'bswap64', 'clz', 'clzll', 'ctz', 'expect', 'ffs',
|
||||||
|
'ffsll', 'popcount', 'popcountll', 'unreachable']
|
||||||
|
if cc.has_function(b)
|
||||||
|
pre_args += '-DHAVE___BUILTIN_@0@'.format(b.to_upper())
|
||||||
|
endif
|
||||||
|
endforeach
|
||||||
|
|
||||||
|
null_dep = dependency('', required : false)
|
||||||
|
|
||||||
|
vulkan_wsi_args = []
|
||||||
|
vulkan_wsi_deps = []
|
||||||
|
|
||||||
|
with_platform_x11 = true
|
||||||
|
with_platform_wayland = false
|
||||||
|
with_xlib_lease = true
|
||||||
|
|
||||||
|
dep_x11 = dependency('x11')
|
||||||
|
dep_xext = dependency('xext')
|
||||||
|
dep_xcb = dependency('xcb')
|
||||||
|
dep_x11_xcb = dependency('x11-xcb')
|
||||||
|
dep_xcb_dri2 = dependency('xcb-dri2', version : '>= 1.8')
|
||||||
|
dep_libdrm = dependency(
|
||||||
|
'libdrm', version : '>=' + '2.4.81',
|
||||||
|
required : true
|
||||||
|
)
|
||||||
|
|
||||||
|
pre_args += '-DHAVE_DRI3'
|
||||||
|
dep_xcb_dri3 = dependency('xcb-dri3')
|
||||||
|
dep_xcb_present = dependency('xcb-present')
|
||||||
|
# until xcb-dri3 has been around long enough to make a hard-dependency:
|
||||||
|
if (dep_xcb_dri3.version().version_compare('>= 1.13') and
|
||||||
|
dep_xcb_present.version().version_compare('>= 1.13'))
|
||||||
|
pre_args += '-DHAVE_DRI3_MODIFIERS'
|
||||||
|
endif
|
||||||
|
dep_xcb_sync = dependency('xcb-sync')
|
||||||
|
dep_xshmfence = dependency('xshmfence', version : '>= 1.1')
|
||||||
|
|
||||||
|
if with_platform_x11
|
||||||
|
vulkan_wsi_args += ['-DVK_USE_PLATFORM_XCB_KHR', '-DVK_USE_PLATFORM_XLIB_KHR']
|
||||||
|
vulkan_wsi_deps += [
|
||||||
|
dep_xcb,
|
||||||
|
dep_x11_xcb,
|
||||||
|
dep_xcb_dri2,
|
||||||
|
dep_xcb_dri3,
|
||||||
|
dep_xcb_present,
|
||||||
|
dep_xcb_sync,
|
||||||
|
dep_xshmfence,
|
||||||
|
]
|
||||||
|
endif
|
||||||
|
if with_platform_wayland
|
||||||
|
dep_wayland_client = dependency('wayland-client', version : '>=1.11')
|
||||||
|
vulkan_wsi_args += ['-DVK_USE_PLATFORM_WAYLAND_KHR']
|
||||||
|
vulkan_wsi_deps += dep_wayland_client
|
||||||
|
endif
|
||||||
|
|
||||||
|
vulkan_wsi_args += '-DVK_USE_PLATFORM_DISPLAY_KHR'
|
||||||
|
vulkan_wsi_deps += [dep_libdrm]
|
||||||
|
|
||||||
|
if with_xlib_lease
|
||||||
|
dep_xcb_xrandr = dependency('xcb-randr')
|
||||||
|
dep_xlib_xrandr = dependency('xrandr', version : '>= 1.3')
|
||||||
|
vulkan_wsi_args += '-DVK_USE_PLATFORM_XLIB_XRANDR_EXT'
|
||||||
|
vulkan_wsi_deps += [dep_xcb_xrandr, dep_xlib_xrandr]
|
||||||
|
endif
|
||||||
|
|
||||||
|
inc_common = [
|
||||||
|
include_directories('include'),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Check for generic C arguments
|
||||||
|
c_args = []
|
||||||
|
foreach a : ['-Werror=implicit-function-declaration',
|
||||||
|
'-Werror=missing-prototypes', '-Werror=return-type',
|
||||||
|
'-Werror=incompatible-pointer-types',
|
||||||
|
'-fno-math-errno',
|
||||||
|
'-fno-trapping-math', '-Qunused-arguments']
|
||||||
|
if cc.has_argument(a)
|
||||||
|
c_args += a
|
||||||
|
endif
|
||||||
|
endforeach
|
||||||
|
|
||||||
|
foreach a : ['missing-field-initializers', 'format-truncation']
|
||||||
|
if cc.has_argument('-W' + a)
|
||||||
|
c_args += '-Wno-' + a
|
||||||
|
endif
|
||||||
|
endforeach
|
||||||
|
|
||||||
|
c_vis_args = []
|
||||||
|
if cc.has_argument('-fvisibility=hidden')
|
||||||
|
c_vis_args += '-fvisibility=hidden'
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Check for generic C++ arguments
|
||||||
|
cpp_args = []
|
||||||
|
foreach a : ['-Werror=return-type',
|
||||||
|
'-fno-math-errno', '-fno-trapping-math',
|
||||||
|
'-Qunused-arguments']
|
||||||
|
if cpp.has_argument(a)
|
||||||
|
cpp_args += a
|
||||||
|
endif
|
||||||
|
endforeach
|
||||||
|
|
||||||
|
# For some reason, the test for -Wno-foo always succeeds with gcc, even if the
|
||||||
|
# option is not supported. Hence, check for -Wfoo instead.
|
||||||
|
|
||||||
|
foreach a : ['non-virtual-dtor', 'missing-field-initializers', 'format-truncation']
|
||||||
|
if cpp.has_argument('-W' + a)
|
||||||
|
cpp_args += '-Wno-' + a
|
||||||
|
endif
|
||||||
|
endforeach
|
||||||
|
|
||||||
|
no_override_init_args = []
|
||||||
|
foreach a : ['override-init', 'initializer-overrides']
|
||||||
|
if cc.has_argument('-W' + a)
|
||||||
|
no_override_init_args += '-Wno-' + a
|
||||||
|
endif
|
||||||
|
endforeach
|
||||||
|
|
||||||
|
cpp_vis_args = []
|
||||||
|
if cpp.has_argument('-fvisibility=hidden')
|
||||||
|
cpp_vis_args += '-fvisibility=hidden'
|
||||||
|
endif
|
||||||
|
|
||||||
|
foreach a : pre_args
|
||||||
|
add_project_arguments(a, language : ['c', 'cpp'])
|
||||||
|
endforeach
|
||||||
|
foreach a : c_args
|
||||||
|
add_project_arguments(a, language : ['c'])
|
||||||
|
endforeach
|
||||||
|
foreach a : cpp_args
|
||||||
|
add_project_arguments(a, language : ['cpp'])
|
||||||
|
endforeach
|
||||||
|
|
||||||
|
# check for dl support
|
||||||
|
if cc.has_function('dlopen')
|
||||||
|
dep_dl = null_dep
|
||||||
|
else
|
||||||
|
dep_dl = cc.find_library('dl')
|
||||||
|
endif
|
||||||
|
|
||||||
|
dep_pthread = cc.find_library('pthread')
|
||||||
|
|
||||||
|
git_sha1_gen_py = files('bin/git_sha1_gen.py')
|
||||||
|
sha1_h = custom_target(
|
||||||
|
'git_sha1.h',
|
||||||
|
output : 'git_sha1.h',
|
||||||
|
command : [prog_python, git_sha1_gen_py, '--output', '@OUTPUT@'],
|
||||||
|
build_always : true, # commit sha1 can change without having touched these files
|
||||||
|
)
|
||||||
|
|
||||||
|
vk_layer_table_helpers = []
|
||||||
|
loader_genvk_py = files('modules/Vulkan-Loader/scripts/loader_genvk.py')
|
||||||
|
foreach s : ['vk_dispatch_table_helper.h', 'vk_layer_dispatch_table.h']#, 'vk_loader_extensions.h', 'vk_loader_extensions.c']
|
||||||
|
vk_layer_table_helpers += custom_target(
|
||||||
|
s, output : s,
|
||||||
|
command : [prog_python, loader_genvk_py,
|
||||||
|
'-scripts', '../../Vulkan-Docs/scripts', # relative to loader_genvk.py
|
||||||
|
'-registry', join_paths(meson.source_root(), 'modules/Vulkan-Docs/xml/vk.xml'),
|
||||||
|
'-o','@OUTDIR@', s])
|
||||||
|
endforeach
|
||||||
|
|
||||||
|
vk_api_xml = files('modules/Vulkan-Docs/xml/vk.xml')
|
||||||
|
vk_enum_to_str = custom_target(
|
||||||
|
'vk_enum_to_str',
|
||||||
|
input : ['bin/gen_enum_to_str.py', vk_api_xml],
|
||||||
|
output : ['vk_enum_to_str.c', 'vk_enum_to_str.h'],
|
||||||
|
command : [
|
||||||
|
prog_python, '@INPUT0@', '--xml', '@INPUT1@',
|
||||||
|
'--outdir', meson.current_build_dir()
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
util_files = files(
|
||||||
|
'src/mesa/util/hash_table.c',
|
||||||
|
'src/mesa/util/os_socket.c',
|
||||||
|
'src/mesa/util/os_time.c',
|
||||||
|
'src/mesa/util/ralloc.c',
|
||||||
|
'src/mesa/main/hash.c',
|
||||||
|
)
|
||||||
|
|
||||||
|
subdir('modules/ImGui')
|
||||||
|
subdir('src')
|
@ -0,0 +1,265 @@
|
|||||||
|
#include <cmath>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iterator>
|
||||||
|
#include <thread>
|
||||||
|
#include <sstream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
#include <regex>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int gpuLoad, gpuTemp, cpuTemp;
|
||||||
|
string gpuLoadDisplay, cpuTempLocation;
|
||||||
|
FILE *amdGpuFile, *amdTempFile, *cpuTempFile;
|
||||||
|
|
||||||
|
const int NUM_CPU_STATES = 10;
|
||||||
|
|
||||||
|
struct Cpus{
|
||||||
|
size_t num;
|
||||||
|
string name;
|
||||||
|
int value;
|
||||||
|
string output;
|
||||||
|
int freq;
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t numCpuCores = std::thread::hardware_concurrency();
|
||||||
|
size_t arraySize = numCpuCores + 1;
|
||||||
|
std::vector<Cpus> cpuArray;
|
||||||
|
pthread_t cpuThread, gpuThread, cpuInfoThread, nvidiaSmiThread;
|
||||||
|
|
||||||
|
string exec(string command) {
|
||||||
|
char buffer[128];
|
||||||
|
string result = "";
|
||||||
|
|
||||||
|
// Open pipe to file
|
||||||
|
FILE* pipe = popen(command.c_str(), "r");
|
||||||
|
if (!pipe) {
|
||||||
|
return "popen failed!";
|
||||||
|
}
|
||||||
|
|
||||||
|
// read till end of process:
|
||||||
|
while (!feof(pipe)) {
|
||||||
|
|
||||||
|
// use buffer to read and add to result
|
||||||
|
if (fgets(buffer, 128, pipe) != NULL)
|
||||||
|
result += buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
pclose(pipe);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void coreCounting(){
|
||||||
|
cpuArray.push_back({0, "CPU:"});
|
||||||
|
for (size_t i = 0; i < arraySize; i++) {
|
||||||
|
size_t offset = i;
|
||||||
|
stringstream ss;
|
||||||
|
ss << "CPU " << offset << ":";
|
||||||
|
string cpuNameString = ss.str();
|
||||||
|
cpuArray.push_back({i+1 , cpuNameString});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string m_cpuUtilizationString;
|
||||||
|
|
||||||
|
enum CPUStates
|
||||||
|
{
|
||||||
|
S_USER = 0,
|
||||||
|
S_NICE,
|
||||||
|
S_SYSTEM,
|
||||||
|
S_IDLE,
|
||||||
|
S_IOWAIT,
|
||||||
|
S_IRQ,
|
||||||
|
S_SOFTIRQ,
|
||||||
|
S_STEAL,
|
||||||
|
S_GUEST,
|
||||||
|
S_GUEST_NICE
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct CPUData
|
||||||
|
{
|
||||||
|
std::string cpu;
|
||||||
|
size_t times[NUM_CPU_STATES];
|
||||||
|
} CPUData;
|
||||||
|
|
||||||
|
void ReadStatsCPU(std::vector<CPUData> & entries)
|
||||||
|
{
|
||||||
|
std::ifstream fileStat("/proc/stat");
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
|
||||||
|
const std::string STR_CPU("cpu");
|
||||||
|
const std::size_t LEN_STR_CPU = STR_CPU.size();
|
||||||
|
const std::string STR_TOT("tot");
|
||||||
|
|
||||||
|
while(std::getline(fileStat, line))
|
||||||
|
{
|
||||||
|
// cpu stats line found
|
||||||
|
if(!line.compare(0, LEN_STR_CPU, STR_CPU))
|
||||||
|
{
|
||||||
|
std::istringstream ss(line);
|
||||||
|
|
||||||
|
// store entry
|
||||||
|
entries.emplace_back(CPUData());
|
||||||
|
CPUData & entry = entries.back();
|
||||||
|
|
||||||
|
// read cpu label
|
||||||
|
ss >> entry.cpu;
|
||||||
|
|
||||||
|
if(entry.cpu.size() > LEN_STR_CPU)
|
||||||
|
entry.cpu.erase(0, LEN_STR_CPU);
|
||||||
|
else
|
||||||
|
entry.cpu = STR_TOT;
|
||||||
|
|
||||||
|
// read times
|
||||||
|
for(int i = 0; i < NUM_CPU_STATES; ++i)
|
||||||
|
ss >> entry.times[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetIdleTime(const CPUData & e)
|
||||||
|
{
|
||||||
|
return e.times[S_IDLE] +
|
||||||
|
e.times[S_IOWAIT];
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetActiveTime(const CPUData & e)
|
||||||
|
{
|
||||||
|
return e.times[S_USER] +
|
||||||
|
e.times[S_NICE] +
|
||||||
|
e.times[S_SYSTEM] +
|
||||||
|
e.times[S_IRQ] +
|
||||||
|
e.times[S_SOFTIRQ] +
|
||||||
|
e.times[S_STEAL] +
|
||||||
|
e.times[S_GUEST] +
|
||||||
|
e.times[S_GUEST_NICE];
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintStats(const std::vector<CPUData> & entries1, const std::vector<CPUData> & entries2)
|
||||||
|
{
|
||||||
|
const size_t NUM_ENTRIES = entries1.size();
|
||||||
|
|
||||||
|
for(size_t i = 0; i < NUM_ENTRIES; ++i)
|
||||||
|
{
|
||||||
|
const CPUData & e1 = entries1[i];
|
||||||
|
const CPUData & e2 = entries2[i];
|
||||||
|
|
||||||
|
const float ACTIVE_TIME = static_cast<float>(GetActiveTime(e2) - GetActiveTime(e1));
|
||||||
|
const float IDLE_TIME = static_cast<float>(GetIdleTime(e2) - GetIdleTime(e1));
|
||||||
|
const float TOTAL_TIME = ACTIVE_TIME + IDLE_TIME;
|
||||||
|
|
||||||
|
cpuArray[i].value = (truncf(100.f * ACTIVE_TIME / TOTAL_TIME) * 10 / 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void *cpuInfo(void *){
|
||||||
|
FILE *cpuInfo = fopen("/proc/cpuinfo", "r");
|
||||||
|
char line[256];
|
||||||
|
int i = 0;
|
||||||
|
while (fgets(line, sizeof(line), cpuInfo)) {
|
||||||
|
std::string row;
|
||||||
|
row = line;
|
||||||
|
if (row.find("MHz") != std::string::npos){
|
||||||
|
row = std::regex_replace(row, std::regex(R"([^0-9.])"), "");
|
||||||
|
cpuArray[i + 1].freq = stoi(row);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(cpuInfo);
|
||||||
|
|
||||||
|
char buff[6];
|
||||||
|
rewind(cpuTempFile);
|
||||||
|
fflush(cpuTempFile);
|
||||||
|
fscanf(cpuTempFile, "%s", buff);
|
||||||
|
cpuTemp = stoi(buff) / 1000;
|
||||||
|
pthread_detach(cpuInfoThread);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *queryNvidiaSmi(void *){
|
||||||
|
vector<string> smiArray;
|
||||||
|
string nvidiaSmi = exec("nvidia-smi --query-gpu=utilization.gpu,temperature.gpu --format=csv,noheader | tr -d ' ' | head -n1 | tr -d '%'");
|
||||||
|
istringstream f(nvidiaSmi);
|
||||||
|
string s;
|
||||||
|
while (getline(f, s, ',')) {
|
||||||
|
smiArray.push_back(s);
|
||||||
|
}
|
||||||
|
gpuLoadDisplay = smiArray[0];
|
||||||
|
gpuLoad = stoi(smiArray[0]);
|
||||||
|
gpuTemp = stoi(smiArray[1]);
|
||||||
|
|
||||||
|
pthread_detach(nvidiaSmiThread);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *getAmdGpuUsage(void *){
|
||||||
|
char buff[5];
|
||||||
|
rewind(amdGpuFile);
|
||||||
|
fflush(amdGpuFile);
|
||||||
|
fscanf(amdGpuFile, "%s", buff);
|
||||||
|
gpuLoadDisplay = buff;
|
||||||
|
gpuLoad = stoi(buff);
|
||||||
|
|
||||||
|
rewind(amdTempFile);
|
||||||
|
fflush(amdTempFile);
|
||||||
|
fscanf(amdTempFile, "%s", buff);
|
||||||
|
gpuTemp = (stoi(buff) / 1000);
|
||||||
|
|
||||||
|
pthread_detach(gpuThread);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *getCpuUsage(void *)
|
||||||
|
{
|
||||||
|
std::vector<CPUData> entries1;
|
||||||
|
std::vector<CPUData> entries2;
|
||||||
|
|
||||||
|
// snapshot 1
|
||||||
|
ReadStatsCPU(entries1);
|
||||||
|
|
||||||
|
// 100ms pause
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
|
// snapshot 2
|
||||||
|
ReadStatsCPU(entries2);
|
||||||
|
|
||||||
|
// print output
|
||||||
|
PrintStats(entries1, entries2);
|
||||||
|
pthread_detach(cpuThread);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void updateCpuStrings(){
|
||||||
|
for (size_t i = 0; i < arraySize; i++) {
|
||||||
|
size_t spacing = 10;
|
||||||
|
string value = to_string(cpuArray[i].value);
|
||||||
|
value.erase( value.find_last_not_of('0') + 1, std::string::npos );
|
||||||
|
size_t correctionValue = (spacing - cpuArray[i].name.length()) - value.length();
|
||||||
|
string correction = "";
|
||||||
|
for (size_t i = 0; i < correctionValue; i++) {
|
||||||
|
correction.append(" ");
|
||||||
|
}
|
||||||
|
stringstream ss;
|
||||||
|
if (i < 11) {
|
||||||
|
if (i == 0) {
|
||||||
|
ss << cpuArray[i].name << " " << cpuArray[i].value << "%";
|
||||||
|
} else {
|
||||||
|
ss << cpuArray[i].name << correction << cpuArray[i].value << "%";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ss << cpuArray[i].name << correction << cpuArray[i].value << "%";
|
||||||
|
}
|
||||||
|
cpuArray[i].output = ss.str();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include "X11/keysym.h"
|
||||||
|
#include "mesa/util/os_time.h"
|
||||||
|
|
||||||
|
double elapsedF2, elapsedF12;
|
||||||
|
uint64_t last_f2_press, last_f12_press;
|
||||||
|
pthread_t f2;
|
||||||
|
char *displayid = getenv("DISPLAY");
|
||||||
|
Display *dpy = XOpenDisplay(displayid);
|
||||||
|
|
||||||
|
bool key_is_pressed(KeySym ks) {
|
||||||
|
char keys_return[32];
|
||||||
|
XQueryKeymap(dpy, keys_return);
|
||||||
|
KeyCode kc2 = XKeysymToKeycode(dpy, ks);
|
||||||
|
bool isPressed = !!(keys_return[kc2 >> 3] & (1 << (kc2 & 7)));
|
||||||
|
return isPressed;
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <fstream>
|
||||||
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "mesa/util/os_time.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
string os, cpu, gpu, ram, kernel, driver, deviceName;
|
||||||
|
bool sysInfoFetched;
|
||||||
|
int gpuLoadLog,cpuLoadLog,log_period;
|
||||||
|
|
||||||
|
struct logData{
|
||||||
|
double fps;
|
||||||
|
double cpu;
|
||||||
|
double gpu;
|
||||||
|
double previous;
|
||||||
|
};
|
||||||
|
|
||||||
|
double fps, elapsedLog;
|
||||||
|
std::vector<logData> logArray;
|
||||||
|
ofstream out;
|
||||||
|
const char* duration_env = std::getenv("LOG_DURATION");
|
||||||
|
const char* mangohud_output_env = std::getenv("MANGOHUD_OUTPUT");
|
||||||
|
const char* log_period_env = std::getenv("LOG_PERIOD");
|
||||||
|
int duration, num;
|
||||||
|
bool loggingOn;
|
||||||
|
uint64_t log_start;
|
||||||
|
|
||||||
|
void writeFile(string date){
|
||||||
|
out.open(mangohud_output_env + date, ios::out | ios::app);
|
||||||
|
out << "os," << "cpu," << "gpu," << "ram," << "kernel," << "driver" << endl;
|
||||||
|
out << os << "," << cpu << "," << gpu << "," << ram << "," << kernel << "," << driver << endl;
|
||||||
|
for (size_t i = 0; i < logArray.size(); i++) {
|
||||||
|
out << logArray[i].fps << "," << logArray[i].cpu << "," << logArray[i].gpu << endl;
|
||||||
|
}
|
||||||
|
out.close();
|
||||||
|
logArray.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void *logging(void *){
|
||||||
|
time_t now_log = time(0);
|
||||||
|
tm *log_time = localtime(&now_log);
|
||||||
|
string date = to_string(log_time->tm_year + 1900) + "-" + to_string(1 + log_time->tm_mon) + "-" + to_string(log_time->tm_mday) + "_" + to_string(1 + log_time->tm_hour) + "-" + to_string(1 + log_time->tm_min) + "-" + to_string(1 + log_time->tm_sec);
|
||||||
|
log_start = os_time_get();
|
||||||
|
out.open(mangohud_output_env + date, ios::out | ios::app);
|
||||||
|
|
||||||
|
while (loggingOn){
|
||||||
|
uint64_t now = os_time_get();
|
||||||
|
elapsedLog = (double)(now - log_start);
|
||||||
|
out << fps << "," << cpuLoadLog << "," << gpuLoadLog << "," << now - log_start << endl;
|
||||||
|
// logArray.push_back({fps, cpuLoadLog, gpuLoadLog, 0.0f});
|
||||||
|
|
||||||
|
if ((elapsedLog) >= duration * 1000000 && duration_env)
|
||||||
|
loggingOn = false;
|
||||||
|
|
||||||
|
this_thread::sleep_for(chrono::milliseconds(log_period));
|
||||||
|
}
|
||||||
|
// writeFile(date);
|
||||||
|
out.close();
|
||||||
|
return NULL;
|
||||||
|
}
|
@ -0,0 +1,203 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
import select
|
||||||
|
from select import EPOLLIN, EPOLLPRI, EPOLLERR
|
||||||
|
import time
|
||||||
|
from collections import namedtuple
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
TIMEOUT = 1.0 # seconds
|
||||||
|
|
||||||
|
VERSION_HEADER = bytearray('MesaOverlayControlVersion', 'utf-8')
|
||||||
|
DEVICE_NAME_HEADER = bytearray('DeviceName', 'utf-8')
|
||||||
|
MESA_VERSION_HEADER = bytearray('MesaVersion', 'utf-8')
|
||||||
|
|
||||||
|
DEFAULT_SERVER_ADDRESS = "\0mesa_overlay"
|
||||||
|
|
||||||
|
class Connection:
|
||||||
|
def __init__(self, path):
|
||||||
|
# Create a Unix Domain socket and connect
|
||||||
|
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||||
|
try:
|
||||||
|
sock.connect(path)
|
||||||
|
except socket.error as msg:
|
||||||
|
print(msg)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
self.sock = sock
|
||||||
|
|
||||||
|
# initialize poll interface and register socket
|
||||||
|
epoll = select.epoll()
|
||||||
|
epoll.register(sock, EPOLLIN | EPOLLPRI | EPOLLERR)
|
||||||
|
self.epoll = epoll
|
||||||
|
|
||||||
|
def recv(self, timeout):
|
||||||
|
'''
|
||||||
|
timeout as float in seconds
|
||||||
|
returns:
|
||||||
|
- None on error or disconnection
|
||||||
|
- bytes() (empty) on timeout
|
||||||
|
'''
|
||||||
|
|
||||||
|
events = self.epoll.poll(timeout)
|
||||||
|
for ev in events:
|
||||||
|
(fd, event) = ev
|
||||||
|
if fd != self.sock.fileno():
|
||||||
|
continue
|
||||||
|
|
||||||
|
# check for socket error
|
||||||
|
if event & EPOLLERR:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# EPOLLIN or EPOLLPRI, just read the message
|
||||||
|
msg = self.sock.recv(4096)
|
||||||
|
|
||||||
|
# socket disconnected
|
||||||
|
if len(msg) == 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return msg
|
||||||
|
|
||||||
|
return bytes()
|
||||||
|
|
||||||
|
def send(self, msg):
|
||||||
|
self.sock.send(msg)
|
||||||
|
|
||||||
|
class MsgParser:
|
||||||
|
MSGBEGIN = bytes(':', 'utf-8')[0]
|
||||||
|
MSGEND = bytes(';', 'utf-8')[0]
|
||||||
|
MSGSEP = bytes('=', 'utf-8')[0]
|
||||||
|
|
||||||
|
def __init__(self, conn):
|
||||||
|
self.cmdpos = 0
|
||||||
|
self.parampos = 0
|
||||||
|
self.bufferpos = 0
|
||||||
|
self.reading_cmd = False
|
||||||
|
self.reading_param = False
|
||||||
|
self.buffer = None
|
||||||
|
self.cmd = bytearray(4096)
|
||||||
|
self.param = bytearray(4096)
|
||||||
|
|
||||||
|
self.conn = conn
|
||||||
|
|
||||||
|
def readCmd(self, ncmds, timeout=TIMEOUT):
|
||||||
|
'''
|
||||||
|
returns:
|
||||||
|
- None on error or disconnection
|
||||||
|
- bytes() (empty) on timeout
|
||||||
|
'''
|
||||||
|
|
||||||
|
parsed = []
|
||||||
|
|
||||||
|
remaining = timeout
|
||||||
|
|
||||||
|
while remaining > 0 and ncmds > 0:
|
||||||
|
now = time.monotonic()
|
||||||
|
|
||||||
|
if self.buffer == None:
|
||||||
|
self.buffer = self.conn.recv(remaining)
|
||||||
|
self.bufferpos = 0
|
||||||
|
|
||||||
|
# disconnected or error
|
||||||
|
if self.buffer == None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
for i in range(self.bufferpos, len(self.buffer)):
|
||||||
|
c = self.buffer[i]
|
||||||
|
self.bufferpos += 1
|
||||||
|
if c == self.MSGBEGIN:
|
||||||
|
self.cmdpos = 0
|
||||||
|
self.parampos = 0
|
||||||
|
self.reading_cmd = True
|
||||||
|
self.reading_param = False
|
||||||
|
elif c == self.MSGEND:
|
||||||
|
if not self.reading_cmd:
|
||||||
|
continue
|
||||||
|
self.reading_cmd = False
|
||||||
|
self.reading_param = False
|
||||||
|
|
||||||
|
cmd = self.cmd[0:self.cmdpos]
|
||||||
|
param = self.param[0:self.parampos]
|
||||||
|
self.reading_cmd = False
|
||||||
|
self.reading_param = False
|
||||||
|
|
||||||
|
parsed.append((cmd, param))
|
||||||
|
ncmds -= 1
|
||||||
|
if ncmds == 0:
|
||||||
|
break
|
||||||
|
elif c == self.MSGSEP:
|
||||||
|
if self.reading_cmd:
|
||||||
|
self.reading_param = True
|
||||||
|
else:
|
||||||
|
if self.reading_param:
|
||||||
|
self.param[self.parampos] = c
|
||||||
|
self.parampos += 1
|
||||||
|
elif self.reading_cmd:
|
||||||
|
self.cmd[self.cmdpos] = c
|
||||||
|
self.cmdpos += 1
|
||||||
|
|
||||||
|
# if we read the entire buffer and didn't finish the command,
|
||||||
|
# throw it away
|
||||||
|
self.buffer = None
|
||||||
|
|
||||||
|
# check if we have time for another iteration
|
||||||
|
elapsed = time.monotonic() - now
|
||||||
|
remaining = max(0, remaining - elapsed)
|
||||||
|
|
||||||
|
# timeout
|
||||||
|
return parsed
|
||||||
|
|
||||||
|
def control(args):
|
||||||
|
if args.socket:
|
||||||
|
address = '\0' + args.socket
|
||||||
|
else:
|
||||||
|
address = DEFAULT_SERVER_ADDRESS
|
||||||
|
|
||||||
|
conn = Connection(address)
|
||||||
|
msgparser = MsgParser(conn)
|
||||||
|
|
||||||
|
version = None
|
||||||
|
name = None
|
||||||
|
mesa_version = None
|
||||||
|
|
||||||
|
msgs = msgparser.readCmd(3)
|
||||||
|
|
||||||
|
for m in msgs:
|
||||||
|
cmd, param = m
|
||||||
|
if cmd == VERSION_HEADER:
|
||||||
|
version = int(param)
|
||||||
|
elif cmd == DEVICE_NAME_HEADER:
|
||||||
|
name = param.decode('utf-8')
|
||||||
|
elif cmd == MESA_VERSION_HEADER:
|
||||||
|
mesa_version = param.decode('utf-8')
|
||||||
|
|
||||||
|
if version != 1 or name == None or mesa_version == None:
|
||||||
|
print('ERROR: invalid protocol')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
if args.info:
|
||||||
|
info = "Protocol Version: {}\n"
|
||||||
|
info += "Device Name: {}\n"
|
||||||
|
info += "Mesa Version: {}"
|
||||||
|
print(info.format(version, name, mesa_version))
|
||||||
|
|
||||||
|
if args.cmd == 'start-capture':
|
||||||
|
conn.send(bytearray(':capture=1;', 'utf-8'))
|
||||||
|
elif args.cmd == 'stop-capture':
|
||||||
|
conn.send(bytearray(':capture=0;', 'utf-8'))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser = argparse.ArgumentParser(description='MESA_overlay control client')
|
||||||
|
parser.add_argument('--info', action='store_true', help='Print info from socket')
|
||||||
|
parser.add_argument('--socket', '-s', type=str, help='Path to socket')
|
||||||
|
|
||||||
|
commands = parser.add_subparsers(help='commands to run', dest='cmd')
|
||||||
|
commands.add_parser('start-capture')
|
||||||
|
commands.add_parser('stop-capture')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
control(args)
|
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* C11 <threads.h> emulation library
|
||||||
|
*
|
||||||
|
* (C) Copyright yohhoy 2012.
|
||||||
|
* Distributed under the Boost Software License, Version 1.0.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
* obtaining a copy of the software and accompanying documentation covered by
|
||||||
|
* this license (the "Software") to use, reproduce, display, distribute,
|
||||||
|
* execute, and transmit the Software, and to prepare [[derivative work]]s of the
|
||||||
|
* Software, and to permit third-parties to whom the Software is furnished to
|
||||||
|
* do so, all subject to the following:
|
||||||
|
*
|
||||||
|
* The copyright notices in the Software and this entire statement, including
|
||||||
|
* the above license grant, this restriction and the following disclaimer,
|
||||||
|
* must be included in all copies of the Software, in whole or in part, and
|
||||||
|
* all derivative works of the Software, unless such copies or derivative
|
||||||
|
* works are solely in the form of machine-executable object code generated by
|
||||||
|
* a source language processor.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
* SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
* FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef EMULATED_THREADS_H_INCLUDED_
|
||||||
|
#define EMULATED_THREADS_H_INCLUDED_
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#ifndef TIME_UTC
|
||||||
|
#define TIME_UTC 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "../c99_compat.h" /* for `inline` */
|
||||||
|
|
||||||
|
/*---------------------------- types ----------------------------*/
|
||||||
|
typedef void (*tss_dtor_t)(void*);
|
||||||
|
typedef int (*thrd_start_t)(void*);
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------- enumeration constants --------------------*/
|
||||||
|
enum {
|
||||||
|
mtx_plain = 0,
|
||||||
|
mtx_try = 1,
|
||||||
|
mtx_timed = 2,
|
||||||
|
mtx_recursive = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
thrd_success = 0, // succeeded
|
||||||
|
thrd_timeout, // timeout
|
||||||
|
thrd_error, // failed
|
||||||
|
thrd_busy, // resource busy
|
||||||
|
thrd_nomem // out of memory
|
||||||
|
};
|
||||||
|
|
||||||
|
/*-------------------------- functions --------------------------*/
|
||||||
|
|
||||||
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||||
|
#include "threads_win32.h"
|
||||||
|
#elif defined(HAVE_PTHREAD)
|
||||||
|
#include "threads_posix.h"
|
||||||
|
#else
|
||||||
|
#error Not supported on this platform.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* EMULATED_THREADS_H_INCLUDED_ */
|
@ -0,0 +1,396 @@
|
|||||||
|
/*
|
||||||
|
* C11 <threads.h> emulation library
|
||||||
|
*
|
||||||
|
* (C) Copyright yohhoy 2012.
|
||||||
|
* Distributed under the Boost Software License, Version 1.0.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
* obtaining a copy of the software and accompanying documentation covered by
|
||||||
|
* this license (the "Software") to use, reproduce, display, distribute,
|
||||||
|
* execute, and transmit the Software, and to prepare [[derivative work]]s of the
|
||||||
|
* Software, and to permit third-parties to whom the Software is furnished to
|
||||||
|
* do so, all subject to the following:
|
||||||
|
*
|
||||||
|
* The copyright notices in the Software and this entire statement, including
|
||||||
|
* the above license grant, this restriction and the following disclaimer,
|
||||||
|
* must be included in all copies of the Software, in whole or in part, and
|
||||||
|
* all derivative works of the Software, unless such copies or derivative
|
||||||
|
* works are solely in the form of machine-executable object code generated by
|
||||||
|
* a source language processor.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
* SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
* FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#ifndef assert
|
||||||
|
#include <assert.h>
|
||||||
|
#endif
|
||||||
|
#include <limits.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <stdint.h> /* for intptr_t */
|
||||||
|
|
||||||
|
/*
|
||||||
|
Configuration macro:
|
||||||
|
|
||||||
|
EMULATED_THREADS_USE_NATIVE_TIMEDLOCK
|
||||||
|
Use pthread_mutex_timedlock() for `mtx_timedlock()'
|
||||||
|
Otherwise use mtx_trylock() + *busy loop* emulation.
|
||||||
|
*/
|
||||||
|
#if !defined(__CYGWIN__) && !defined(__APPLE__) && !defined(__NetBSD__)
|
||||||
|
#define EMULATED_THREADS_USE_NATIVE_TIMEDLOCK
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
/*---------------------------- macros ----------------------------*/
|
||||||
|
#define ONCE_FLAG_INIT PTHREAD_ONCE_INIT
|
||||||
|
#ifdef INIT_ONCE_STATIC_INIT
|
||||||
|
#define TSS_DTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS
|
||||||
|
#else
|
||||||
|
#define TSS_DTOR_ITERATIONS 1 // assume TSS dtor MAY be called at least once.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// FIXME: temporary non-standard hack to ease transition
|
||||||
|
#define _MTX_INITIALIZER_NP PTHREAD_MUTEX_INITIALIZER
|
||||||
|
|
||||||
|
/*---------------------------- types ----------------------------*/
|
||||||
|
typedef pthread_cond_t cnd_t;
|
||||||
|
typedef pthread_t thrd_t;
|
||||||
|
typedef pthread_key_t tss_t;
|
||||||
|
typedef pthread_mutex_t mtx_t;
|
||||||
|
typedef pthread_once_t once_flag;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Implementation limits:
|
||||||
|
- Conditionally emulation for "mutex with timeout"
|
||||||
|
(see EMULATED_THREADS_USE_NATIVE_TIMEDLOCK macro)
|
||||||
|
*/
|
||||||
|
struct impl_thrd_param {
|
||||||
|
thrd_start_t func;
|
||||||
|
void *arg;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void *
|
||||||
|
impl_thrd_routine(void *p)
|
||||||
|
{
|
||||||
|
struct impl_thrd_param pack = *((struct impl_thrd_param *)p);
|
||||||
|
free(p);
|
||||||
|
return (void*)(intptr_t)pack.func(pack.arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------- 7.25.2 Initialization functions ---------------*/
|
||||||
|
// 7.25.2.1
|
||||||
|
static inline void
|
||||||
|
call_once(once_flag *flag, void (*func)(void))
|
||||||
|
{
|
||||||
|
pthread_once(flag, func);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*------------- 7.25.3 Condition variable functions -------------*/
|
||||||
|
// 7.25.3.1
|
||||||
|
static inline int
|
||||||
|
cnd_broadcast(cnd_t *cond)
|
||||||
|
{
|
||||||
|
assert(cond != NULL);
|
||||||
|
return (pthread_cond_broadcast(cond) == 0) ? thrd_success : thrd_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.3.2
|
||||||
|
static inline void
|
||||||
|
cnd_destroy(cnd_t *cond)
|
||||||
|
{
|
||||||
|
assert(cond);
|
||||||
|
pthread_cond_destroy(cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.3.3
|
||||||
|
static inline int
|
||||||
|
cnd_init(cnd_t *cond)
|
||||||
|
{
|
||||||
|
assert(cond != NULL);
|
||||||
|
return (pthread_cond_init(cond, NULL) == 0) ? thrd_success : thrd_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.3.4
|
||||||
|
static inline int
|
||||||
|
cnd_signal(cnd_t *cond)
|
||||||
|
{
|
||||||
|
assert(cond != NULL);
|
||||||
|
return (pthread_cond_signal(cond) == 0) ? thrd_success : thrd_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.3.5
|
||||||
|
static inline int
|
||||||
|
cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *abs_time)
|
||||||
|
{
|
||||||
|
int rt;
|
||||||
|
|
||||||
|
assert(mtx != NULL);
|
||||||
|
assert(cond != NULL);
|
||||||
|
assert(abs_time != NULL);
|
||||||
|
|
||||||
|
rt = pthread_cond_timedwait(cond, mtx, abs_time);
|
||||||
|
if (rt == ETIMEDOUT)
|
||||||
|
return thrd_busy;
|
||||||
|
return (rt == 0) ? thrd_success : thrd_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.3.6
|
||||||
|
static inline int
|
||||||
|
cnd_wait(cnd_t *cond, mtx_t *mtx)
|
||||||
|
{
|
||||||
|
assert(mtx != NULL);
|
||||||
|
assert(cond != NULL);
|
||||||
|
return (pthread_cond_wait(cond, mtx) == 0) ? thrd_success : thrd_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------- 7.25.4 Mutex functions --------------------*/
|
||||||
|
// 7.25.4.1
|
||||||
|
static inline void
|
||||||
|
mtx_destroy(mtx_t *mtx)
|
||||||
|
{
|
||||||
|
assert(mtx != NULL);
|
||||||
|
pthread_mutex_destroy(mtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XXX: Workaround when building with -O0 and without pthreads link.
|
||||||
|
*
|
||||||
|
* In such cases constant folding and dead code elimination won't be
|
||||||
|
* available, thus the compiler will always add the pthread_mutexattr*
|
||||||
|
* functions into the binary. As we try to link, we'll fail as the
|
||||||
|
* symbols are unresolved.
|
||||||
|
*
|
||||||
|
* Ideally we'll enable the optimisations locally, yet that does not
|
||||||
|
* seem to work.
|
||||||
|
*
|
||||||
|
* So the alternative workaround is to annotate the symbols as weak.
|
||||||
|
* Thus the linker will be happy and things don't clash when building
|
||||||
|
* with -O1 or greater.
|
||||||
|
*/
|
||||||
|
#if defined(HAVE_FUNC_ATTRIBUTE_WEAK) && !defined(__CYGWIN__)
|
||||||
|
__attribute__((weak))
|
||||||
|
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
|
||||||
|
|
||||||
|
__attribute__((weak))
|
||||||
|
int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);
|
||||||
|
|
||||||
|
__attribute__((weak))
|
||||||
|
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// 7.25.4.2
|
||||||
|
static inline int
|
||||||
|
mtx_init(mtx_t *mtx, int type)
|
||||||
|
{
|
||||||
|
pthread_mutexattr_t attr;
|
||||||
|
assert(mtx != NULL);
|
||||||
|
if (type != mtx_plain && type != mtx_timed && type != mtx_try
|
||||||
|
&& type != (mtx_plain|mtx_recursive)
|
||||||
|
&& type != (mtx_timed|mtx_recursive)
|
||||||
|
&& type != (mtx_try|mtx_recursive))
|
||||||
|
return thrd_error;
|
||||||
|
|
||||||
|
if ((type & mtx_recursive) == 0) {
|
||||||
|
pthread_mutex_init(mtx, NULL);
|
||||||
|
return thrd_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutexattr_init(&attr);
|
||||||
|
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||||
|
pthread_mutex_init(mtx, &attr);
|
||||||
|
pthread_mutexattr_destroy(&attr);
|
||||||
|
return thrd_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.4.3
|
||||||
|
static inline int
|
||||||
|
mtx_lock(mtx_t *mtx)
|
||||||
|
{
|
||||||
|
assert(mtx != NULL);
|
||||||
|
return (pthread_mutex_lock(mtx) == 0) ? thrd_success : thrd_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
mtx_trylock(mtx_t *mtx);
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
thrd_yield(void);
|
||||||
|
|
||||||
|
// 7.25.4.4
|
||||||
|
static inline int
|
||||||
|
mtx_timedlock(mtx_t *mtx, const struct timespec *ts)
|
||||||
|
{
|
||||||
|
assert(mtx != NULL);
|
||||||
|
assert(ts != NULL);
|
||||||
|
|
||||||
|
{
|
||||||
|
#ifdef EMULATED_THREADS_USE_NATIVE_TIMEDLOCK
|
||||||
|
int rt;
|
||||||
|
rt = pthread_mutex_timedlock(mtx, ts);
|
||||||
|
if (rt == 0)
|
||||||
|
return thrd_success;
|
||||||
|
return (rt == ETIMEDOUT) ? thrd_busy : thrd_error;
|
||||||
|
#else
|
||||||
|
time_t expire = time(NULL);
|
||||||
|
expire += ts->tv_sec;
|
||||||
|
while (mtx_trylock(mtx) != thrd_success) {
|
||||||
|
time_t now = time(NULL);
|
||||||
|
if (expire < now)
|
||||||
|
return thrd_busy;
|
||||||
|
// busy loop!
|
||||||
|
thrd_yield();
|
||||||
|
}
|
||||||
|
return thrd_success;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.4.5
|
||||||
|
static inline int
|
||||||
|
mtx_trylock(mtx_t *mtx)
|
||||||
|
{
|
||||||
|
assert(mtx != NULL);
|
||||||
|
return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.4.6
|
||||||
|
static inline int
|
||||||
|
mtx_unlock(mtx_t *mtx)
|
||||||
|
{
|
||||||
|
assert(mtx != NULL);
|
||||||
|
return (pthread_mutex_unlock(mtx) == 0) ? thrd_success : thrd_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*------------------- 7.25.5 Thread functions -------------------*/
|
||||||
|
// 7.25.5.1
|
||||||
|
static inline int
|
||||||
|
thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
|
||||||
|
{
|
||||||
|
struct impl_thrd_param *pack;
|
||||||
|
assert(thr != NULL);
|
||||||
|
pack = (struct impl_thrd_param *)malloc(sizeof(struct impl_thrd_param));
|
||||||
|
if (!pack) return thrd_nomem;
|
||||||
|
pack->func = func;
|
||||||
|
pack->arg = arg;
|
||||||
|
if (pthread_create(thr, NULL, impl_thrd_routine, pack) != 0) {
|
||||||
|
free(pack);
|
||||||
|
return thrd_error;
|
||||||
|
}
|
||||||
|
return thrd_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.5.2
|
||||||
|
static inline thrd_t
|
||||||
|
thrd_current(void)
|
||||||
|
{
|
||||||
|
return pthread_self();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.5.3
|
||||||
|
static inline int
|
||||||
|
thrd_detach(thrd_t thr)
|
||||||
|
{
|
||||||
|
return (pthread_detach(thr) == 0) ? thrd_success : thrd_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.5.4
|
||||||
|
static inline int
|
||||||
|
thrd_equal(thrd_t thr0, thrd_t thr1)
|
||||||
|
{
|
||||||
|
return pthread_equal(thr0, thr1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.5.5
|
||||||
|
static inline void
|
||||||
|
thrd_exit(int res)
|
||||||
|
{
|
||||||
|
pthread_exit((void*)(intptr_t)res);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.5.6
|
||||||
|
static inline int
|
||||||
|
thrd_join(thrd_t thr, int *res)
|
||||||
|
{
|
||||||
|
void *code;
|
||||||
|
if (pthread_join(thr, &code) != 0)
|
||||||
|
return thrd_error;
|
||||||
|
if (res)
|
||||||
|
*res = (int)(intptr_t)code;
|
||||||
|
return thrd_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.5.7
|
||||||
|
static inline void
|
||||||
|
thrd_sleep(const struct timespec *time_point, struct timespec *remaining)
|
||||||
|
{
|
||||||
|
assert(time_point != NULL);
|
||||||
|
nanosleep(time_point, remaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.5.8
|
||||||
|
static inline void
|
||||||
|
thrd_yield(void)
|
||||||
|
{
|
||||||
|
sched_yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*----------- 7.25.6 Thread-specific storage functions -----------*/
|
||||||
|
// 7.25.6.1
|
||||||
|
static inline int
|
||||||
|
tss_create(tss_t *key, tss_dtor_t dtor)
|
||||||
|
{
|
||||||
|
assert(key != NULL);
|
||||||
|
return (pthread_key_create(key, dtor) == 0) ? thrd_success : thrd_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.6.2
|
||||||
|
static inline void
|
||||||
|
tss_delete(tss_t key)
|
||||||
|
{
|
||||||
|
pthread_key_delete(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.6.3
|
||||||
|
static inline void *
|
||||||
|
tss_get(tss_t key)
|
||||||
|
{
|
||||||
|
return pthread_getspecific(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.6.4
|
||||||
|
static inline int
|
||||||
|
tss_set(tss_t key, void *val)
|
||||||
|
{
|
||||||
|
return (pthread_setspecific(key, val) == 0) ? thrd_success : thrd_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------- 7.25.7 Time functions --------------------*/
|
||||||
|
// 7.25.6.1
|
||||||
|
#ifndef HAVE_TIMESPEC_GET
|
||||||
|
static inline int
|
||||||
|
timespec_get(struct timespec *ts, int base)
|
||||||
|
{
|
||||||
|
if (!ts) return 0;
|
||||||
|
if (base == TIME_UTC) {
|
||||||
|
clock_gettime(CLOCK_REALTIME, ts);
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
@ -0,0 +1,653 @@
|
|||||||
|
/*
|
||||||
|
* C11 <threads.h> emulation library
|
||||||
|
*
|
||||||
|
* (C) Copyright yohhoy 2012.
|
||||||
|
* Distributed under the Boost Software License, Version 1.0.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
* obtaining a copy of the software and accompanying documentation covered by
|
||||||
|
* this license (the "Software") to use, reproduce, display, distribute,
|
||||||
|
* execute, and transmit the Software, and to prepare [[derivative work]]s of the
|
||||||
|
* Software, and to permit third-parties to whom the Software is furnished to
|
||||||
|
* do so, all subject to the following:
|
||||||
|
*
|
||||||
|
* The copyright notices in the Software and this entire statement, including
|
||||||
|
* the above license grant, this restriction and the following disclaimer,
|
||||||
|
* must be included in all copies of the Software, in whole or in part, and
|
||||||
|
* all derivative works of the Software, unless such copies or derivative
|
||||||
|
* works are solely in the form of machine-executable object code generated by
|
||||||
|
* a source language processor.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
* SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
* FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef assert
|
||||||
|
#include <assert.h>
|
||||||
|
#endif
|
||||||
|
#include <limits.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <process.h> // MSVCRT
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
Configuration macro:
|
||||||
|
|
||||||
|
EMULATED_THREADS_USE_NATIVE_CALL_ONCE
|
||||||
|
Use native WindowsAPI one-time initialization function.
|
||||||
|
(requires WinVista or later)
|
||||||
|
Otherwise emulate by mtx_trylock() + *busy loop* for WinXP.
|
||||||
|
|
||||||
|
EMULATED_THREADS_USE_NATIVE_CV
|
||||||
|
Use native WindowsAPI condition variable object.
|
||||||
|
(requires WinVista or later)
|
||||||
|
Otherwise use emulated implementation for WinXP.
|
||||||
|
|
||||||
|
EMULATED_THREADS_TSS_DTOR_SLOTNUM
|
||||||
|
Max registerable TSS dtor number.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// XXX: Retain XP compatability
|
||||||
|
#if 0
|
||||||
|
#if _WIN32_WINNT >= 0x0600
|
||||||
|
// Prefer native WindowsAPI on newer environment.
|
||||||
|
#if !defined(__MINGW32__)
|
||||||
|
#define EMULATED_THREADS_USE_NATIVE_CALL_ONCE
|
||||||
|
#endif
|
||||||
|
#define EMULATED_THREADS_USE_NATIVE_CV
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#define EMULATED_THREADS_TSS_DTOR_SLOTNUM 64 // see TLS_MINIMUM_AVAILABLE
|
||||||
|
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
// check configuration
|
||||||
|
#if defined(EMULATED_THREADS_USE_NATIVE_CALL_ONCE) && (_WIN32_WINNT < 0x0600)
|
||||||
|
#error EMULATED_THREADS_USE_NATIVE_CALL_ONCE requires _WIN32_WINNT>=0x0600
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(EMULATED_THREADS_USE_NATIVE_CV) && (_WIN32_WINNT < 0x0600)
|
||||||
|
#error EMULATED_THREADS_USE_NATIVE_CV requires _WIN32_WINNT>=0x0600
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Visual Studio 2015 and later */
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define HAVE_TIMESPEC_GET
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*---------------------------- macros ----------------------------*/
|
||||||
|
#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
|
||||||
|
#define ONCE_FLAG_INIT INIT_ONCE_STATIC_INIT
|
||||||
|
#else
|
||||||
|
#define ONCE_FLAG_INIT {0}
|
||||||
|
#endif
|
||||||
|
#define TSS_DTOR_ITERATIONS 1
|
||||||
|
|
||||||
|
// FIXME: temporary non-standard hack to ease transition
|
||||||
|
#define _MTX_INITIALIZER_NP {(PCRITICAL_SECTION_DEBUG)-1, -1, 0, 0, 0, 0}
|
||||||
|
|
||||||
|
/*---------------------------- types ----------------------------*/
|
||||||
|
typedef struct cnd_t {
|
||||||
|
#ifdef EMULATED_THREADS_USE_NATIVE_CV
|
||||||
|
CONDITION_VARIABLE condvar;
|
||||||
|
#else
|
||||||
|
int blocked;
|
||||||
|
int gone;
|
||||||
|
int to_unblock;
|
||||||
|
HANDLE sem_queue;
|
||||||
|
HANDLE sem_gate;
|
||||||
|
CRITICAL_SECTION monitor;
|
||||||
|
#endif
|
||||||
|
} cnd_t;
|
||||||
|
|
||||||
|
typedef HANDLE thrd_t;
|
||||||
|
|
||||||
|
typedef DWORD tss_t;
|
||||||
|
|
||||||
|
typedef CRITICAL_SECTION mtx_t;
|
||||||
|
|
||||||
|
#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
|
||||||
|
typedef INIT_ONCE once_flag;
|
||||||
|
#else
|
||||||
|
typedef struct once_flag_t {
|
||||||
|
volatile LONG status;
|
||||||
|
} once_flag;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static inline void * tss_get(tss_t key);
|
||||||
|
static inline void thrd_yield(void);
|
||||||
|
static inline int mtx_trylock(mtx_t *mtx);
|
||||||
|
static inline int mtx_lock(mtx_t *mtx);
|
||||||
|
static inline int mtx_unlock(mtx_t *mtx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Implementation limits:
|
||||||
|
- Conditionally emulation for "Initialization functions"
|
||||||
|
(see EMULATED_THREADS_USE_NATIVE_CALL_ONCE macro)
|
||||||
|
- Emulated `mtx_timelock()' with mtx_trylock() + *busy loop*
|
||||||
|
*/
|
||||||
|
static void impl_tss_dtor_invoke(void); // forward decl.
|
||||||
|
|
||||||
|
struct impl_thrd_param {
|
||||||
|
thrd_start_t func;
|
||||||
|
void *arg;
|
||||||
|
};
|
||||||
|
|
||||||
|
static unsigned __stdcall impl_thrd_routine(void *p)
|
||||||
|
{
|
||||||
|
struct impl_thrd_param pack;
|
||||||
|
int code;
|
||||||
|
memcpy(&pack, p, sizeof(struct impl_thrd_param));
|
||||||
|
free(p);
|
||||||
|
code = pack.func(pack.arg);
|
||||||
|
impl_tss_dtor_invoke();
|
||||||
|
return (unsigned)code;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DWORD impl_timespec2msec(const struct timespec *ts)
|
||||||
|
{
|
||||||
|
return (DWORD)((ts->tv_sec * 1000U) + (ts->tv_nsec / 1000000L));
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
|
||||||
|
struct impl_call_once_param { void (*func)(void); };
|
||||||
|
static BOOL CALLBACK impl_call_once_callback(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context)
|
||||||
|
{
|
||||||
|
struct impl_call_once_param *param = (struct impl_call_once_param*)Parameter;
|
||||||
|
(param->func)();
|
||||||
|
((void)InitOnce); ((void)Context); // suppress warning
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
#endif // ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
|
||||||
|
|
||||||
|
#ifndef EMULATED_THREADS_USE_NATIVE_CV
|
||||||
|
/*
|
||||||
|
Note:
|
||||||
|
The implementation of condition variable is ported from Boost.Interprocess
|
||||||
|
See http://www.boost.org/boost/interprocess/sync/windows/condition.hpp
|
||||||
|
*/
|
||||||
|
static void impl_cond_do_signal(cnd_t *cond, int broadcast)
|
||||||
|
{
|
||||||
|
int nsignal = 0;
|
||||||
|
|
||||||
|
EnterCriticalSection(&cond->monitor);
|
||||||
|
if (cond->to_unblock != 0) {
|
||||||
|
if (cond->blocked == 0) {
|
||||||
|
LeaveCriticalSection(&cond->monitor);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (broadcast) {
|
||||||
|
cond->to_unblock += nsignal = cond->blocked;
|
||||||
|
cond->blocked = 0;
|
||||||
|
} else {
|
||||||
|
nsignal = 1;
|
||||||
|
cond->to_unblock++;
|
||||||
|
cond->blocked--;
|
||||||
|
}
|
||||||
|
} else if (cond->blocked > cond->gone) {
|
||||||
|
WaitForSingleObject(cond->sem_gate, INFINITE);
|
||||||
|
if (cond->gone != 0) {
|
||||||
|
cond->blocked -= cond->gone;
|
||||||
|
cond->gone = 0;
|
||||||
|
}
|
||||||
|
if (broadcast) {
|
||||||
|
nsignal = cond->to_unblock = cond->blocked;
|
||||||
|
cond->blocked = 0;
|
||||||
|
} else {
|
||||||
|
nsignal = cond->to_unblock = 1;
|
||||||
|
cond->blocked--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LeaveCriticalSection(&cond->monitor);
|
||||||
|
|
||||||
|
if (0 < nsignal)
|
||||||
|
ReleaseSemaphore(cond->sem_queue, nsignal, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int impl_cond_do_wait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts)
|
||||||
|
{
|
||||||
|
int nleft = 0;
|
||||||
|
int ngone = 0;
|
||||||
|
int timeout = 0;
|
||||||
|
DWORD w;
|
||||||
|
|
||||||
|
WaitForSingleObject(cond->sem_gate, INFINITE);
|
||||||
|
cond->blocked++;
|
||||||
|
ReleaseSemaphore(cond->sem_gate, 1, NULL);
|
||||||
|
|
||||||
|
mtx_unlock(mtx);
|
||||||
|
|
||||||
|
w = WaitForSingleObject(cond->sem_queue, ts ? impl_timespec2msec(ts) : INFINITE);
|
||||||
|
timeout = (w == WAIT_TIMEOUT);
|
||||||
|
|
||||||
|
EnterCriticalSection(&cond->monitor);
|
||||||
|
if ((nleft = cond->to_unblock) != 0) {
|
||||||
|
if (timeout) {
|
||||||
|
if (cond->blocked != 0) {
|
||||||
|
cond->blocked--;
|
||||||
|
} else {
|
||||||
|
cond->gone++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (--cond->to_unblock == 0) {
|
||||||
|
if (cond->blocked != 0) {
|
||||||
|
ReleaseSemaphore(cond->sem_gate, 1, NULL);
|
||||||
|
nleft = 0;
|
||||||
|
}
|
||||||
|
else if ((ngone = cond->gone) != 0) {
|
||||||
|
cond->gone = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (++cond->gone == INT_MAX/2) {
|
||||||
|
WaitForSingleObject(cond->sem_gate, INFINITE);
|
||||||
|
cond->blocked -= cond->gone;
|
||||||
|
ReleaseSemaphore(cond->sem_gate, 1, NULL);
|
||||||
|
cond->gone = 0;
|
||||||
|
}
|
||||||
|
LeaveCriticalSection(&cond->monitor);
|
||||||
|
|
||||||
|
if (nleft == 1) {
|
||||||
|
while (ngone--)
|
||||||
|
WaitForSingleObject(cond->sem_queue, INFINITE);
|
||||||
|
ReleaseSemaphore(cond->sem_gate, 1, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
mtx_lock(mtx);
|
||||||
|
return timeout ? thrd_busy : thrd_success;
|
||||||
|
}
|
||||||
|
#endif // ifndef EMULATED_THREADS_USE_NATIVE_CV
|
||||||
|
|
||||||
|
static struct impl_tss_dtor_entry {
|
||||||
|
tss_t key;
|
||||||
|
tss_dtor_t dtor;
|
||||||
|
} impl_tss_dtor_tbl[EMULATED_THREADS_TSS_DTOR_SLOTNUM];
|
||||||
|
|
||||||
|
static int impl_tss_dtor_register(tss_t key, tss_dtor_t dtor)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) {
|
||||||
|
if (!impl_tss_dtor_tbl[i].dtor)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i == EMULATED_THREADS_TSS_DTOR_SLOTNUM)
|
||||||
|
return 1;
|
||||||
|
impl_tss_dtor_tbl[i].key = key;
|
||||||
|
impl_tss_dtor_tbl[i].dtor = dtor;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void impl_tss_dtor_invoke()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) {
|
||||||
|
if (impl_tss_dtor_tbl[i].dtor) {
|
||||||
|
void* val = tss_get(impl_tss_dtor_tbl[i].key);
|
||||||
|
if (val)
|
||||||
|
(impl_tss_dtor_tbl[i].dtor)(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------- 7.25.2 Initialization functions ---------------*/
|
||||||
|
// 7.25.2.1
|
||||||
|
static inline void
|
||||||
|
call_once(once_flag *flag, void (*func)(void))
|
||||||
|
{
|
||||||
|
assert(flag && func);
|
||||||
|
#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
|
||||||
|
{
|
||||||
|
struct impl_call_once_param param;
|
||||||
|
param.func = func;
|
||||||
|
InitOnceExecuteOnce(flag, impl_call_once_callback, (PVOID)¶m, NULL);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (InterlockedCompareExchange(&flag->status, 1, 0) == 0) {
|
||||||
|
(func)();
|
||||||
|
InterlockedExchange(&flag->status, 2);
|
||||||
|
} else {
|
||||||
|
while (flag->status == 1) {
|
||||||
|
// busy loop!
|
||||||
|
thrd_yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*------------- 7.25.3 Condition variable functions -------------*/
|
||||||
|
// 7.25.3.1
|
||||||
|
static inline int
|
||||||
|
cnd_broadcast(cnd_t *cond)
|
||||||
|
{
|
||||||
|
if (!cond) return thrd_error;
|
||||||
|
#ifdef EMULATED_THREADS_USE_NATIVE_CV
|
||||||
|
WakeAllConditionVariable(&cond->condvar);
|
||||||
|
#else
|
||||||
|
impl_cond_do_signal(cond, 1);
|
||||||
|
#endif
|
||||||
|
return thrd_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.3.2
|
||||||
|
static inline void
|
||||||
|
cnd_destroy(cnd_t *cond)
|
||||||
|
{
|
||||||
|
assert(cond);
|
||||||
|
#ifdef EMULATED_THREADS_USE_NATIVE_CV
|
||||||
|
// do nothing
|
||||||
|
#else
|
||||||
|
CloseHandle(cond->sem_queue);
|
||||||
|
CloseHandle(cond->sem_gate);
|
||||||
|
DeleteCriticalSection(&cond->monitor);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.3.3
|
||||||
|
static inline int
|
||||||
|
cnd_init(cnd_t *cond)
|
||||||
|
{
|
||||||
|
if (!cond) return thrd_error;
|
||||||
|
#ifdef EMULATED_THREADS_USE_NATIVE_CV
|
||||||
|
InitializeConditionVariable(&cond->condvar);
|
||||||
|
#else
|
||||||
|
cond->blocked = 0;
|
||||||
|
cond->gone = 0;
|
||||||
|
cond->to_unblock = 0;
|
||||||
|
cond->sem_queue = CreateSemaphore(NULL, 0, LONG_MAX, NULL);
|
||||||
|
cond->sem_gate = CreateSemaphore(NULL, 1, 1, NULL);
|
||||||
|
InitializeCriticalSection(&cond->monitor);
|
||||||
|
#endif
|
||||||
|
return thrd_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.3.4
|
||||||
|
static inline int
|
||||||
|
cnd_signal(cnd_t *cond)
|
||||||
|
{
|
||||||
|
if (!cond) return thrd_error;
|
||||||
|
#ifdef EMULATED_THREADS_USE_NATIVE_CV
|
||||||
|
WakeConditionVariable(&cond->condvar);
|
||||||
|
#else
|
||||||
|
impl_cond_do_signal(cond, 0);
|
||||||
|
#endif
|
||||||
|
return thrd_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.3.5
|
||||||
|
static inline int
|
||||||
|
cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *abs_time)
|
||||||
|
{
|
||||||
|
if (!cond || !mtx || !abs_time) return thrd_error;
|
||||||
|
#ifdef EMULATED_THREADS_USE_NATIVE_CV
|
||||||
|
if (SleepConditionVariableCS(&cond->condvar, mtx, impl_timespec2msec(abs_time)))
|
||||||
|
return thrd_success;
|
||||||
|
return (GetLastError() == ERROR_TIMEOUT) ? thrd_busy : thrd_error;
|
||||||
|
#else
|
||||||
|
return impl_cond_do_wait(cond, mtx, abs_time);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.3.6
|
||||||
|
static inline int
|
||||||
|
cnd_wait(cnd_t *cond, mtx_t *mtx)
|
||||||
|
{
|
||||||
|
if (!cond || !mtx) return thrd_error;
|
||||||
|
#ifdef EMULATED_THREADS_USE_NATIVE_CV
|
||||||
|
SleepConditionVariableCS(&cond->condvar, mtx, INFINITE);
|
||||||
|
#else
|
||||||
|
impl_cond_do_wait(cond, mtx, NULL);
|
||||||
|
#endif
|
||||||
|
return thrd_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------- 7.25.4 Mutex functions --------------------*/
|
||||||
|
// 7.25.4.1
|
||||||
|
static inline void
|
||||||
|
mtx_destroy(mtx_t *mtx)
|
||||||
|
{
|
||||||
|
assert(mtx);
|
||||||
|
DeleteCriticalSection(mtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.4.2
|
||||||
|
static inline int
|
||||||
|
mtx_init(mtx_t *mtx, int type)
|
||||||
|
{
|
||||||
|
if (!mtx) return thrd_error;
|
||||||
|
if (type != mtx_plain && type != mtx_timed && type != mtx_try
|
||||||
|
&& type != (mtx_plain|mtx_recursive)
|
||||||
|
&& type != (mtx_timed|mtx_recursive)
|
||||||
|
&& type != (mtx_try|mtx_recursive))
|
||||||
|
return thrd_error;
|
||||||
|
InitializeCriticalSection(mtx);
|
||||||
|
return thrd_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.4.3
|
||||||
|
static inline int
|
||||||
|
mtx_lock(mtx_t *mtx)
|
||||||
|
{
|
||||||
|
if (!mtx) return thrd_error;
|
||||||
|
EnterCriticalSection(mtx);
|
||||||
|
return thrd_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.4.4
|
||||||
|
static inline int
|
||||||
|
mtx_timedlock(mtx_t *mtx, const struct timespec *ts)
|
||||||
|
{
|
||||||
|
time_t expire, now;
|
||||||
|
if (!mtx || !ts) return thrd_error;
|
||||||
|
expire = time(NULL);
|
||||||
|
expire += ts->tv_sec;
|
||||||
|
while (mtx_trylock(mtx) != thrd_success) {
|
||||||
|
now = time(NULL);
|
||||||
|
if (expire < now)
|
||||||
|
return thrd_busy;
|
||||||
|
// busy loop!
|
||||||
|
thrd_yield();
|
||||||
|
}
|
||||||
|
return thrd_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.4.5
|
||||||
|
static inline int
|
||||||
|
mtx_trylock(mtx_t *mtx)
|
||||||
|
{
|
||||||
|
if (!mtx) return thrd_error;
|
||||||
|
return TryEnterCriticalSection(mtx) ? thrd_success : thrd_busy;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.4.6
|
||||||
|
static inline int
|
||||||
|
mtx_unlock(mtx_t *mtx)
|
||||||
|
{
|
||||||
|
if (!mtx) return thrd_error;
|
||||||
|
LeaveCriticalSection(mtx);
|
||||||
|
return thrd_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*------------------- 7.25.5 Thread functions -------------------*/
|
||||||
|
// 7.25.5.1
|
||||||
|
static inline int
|
||||||
|
thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
|
||||||
|
{
|
||||||
|
struct impl_thrd_param *pack;
|
||||||
|
uintptr_t handle;
|
||||||
|
if (!thr) return thrd_error;
|
||||||
|
pack = (struct impl_thrd_param *)malloc(sizeof(struct impl_thrd_param));
|
||||||
|
if (!pack) return thrd_nomem;
|
||||||
|
pack->func = func;
|
||||||
|
pack->arg = arg;
|
||||||
|
handle = _beginthreadex(NULL, 0, impl_thrd_routine, pack, 0, NULL);
|
||||||
|
if (handle == 0) {
|
||||||
|
if (errno == EAGAIN || errno == EACCES)
|
||||||
|
return thrd_nomem;
|
||||||
|
return thrd_error;
|
||||||
|
}
|
||||||
|
*thr = (thrd_t)handle;
|
||||||
|
return thrd_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// 7.25.5.2
|
||||||
|
static inline thrd_t
|
||||||
|
thrd_current(void)
|
||||||
|
{
|
||||||
|
HANDLE hCurrentThread;
|
||||||
|
BOOL bRet;
|
||||||
|
|
||||||
|
/* GetCurrentThread() returns a pseudo-handle, which we need
|
||||||
|
* to pass to DuplicateHandle(). Only the resulting handle can be used
|
||||||
|
* from other threads.
|
||||||
|
*
|
||||||
|
* Note that neither handle can be compared to the one by thread_create.
|
||||||
|
* Only the thread IDs - as returned by GetThreadId() and GetCurrentThreadId()
|
||||||
|
* can be compared directly.
|
||||||
|
*
|
||||||
|
* Other potential solutions would be:
|
||||||
|
* - define thrd_t as a thread Ids, but this would mean we'd need to OpenThread for many operations
|
||||||
|
* - use malloc'ed memory for thrd_t. This would imply using TLS for current thread.
|
||||||
|
*
|
||||||
|
* Neither is particularly nice.
|
||||||
|
*
|
||||||
|
* Life would be much easier if C11 threads had different abstractions for
|
||||||
|
* threads and thread IDs, just like C++11 threads does...
|
||||||
|
*/
|
||||||
|
|
||||||
|
bRet = DuplicateHandle(GetCurrentProcess(), // source process (pseudo) handle
|
||||||
|
GetCurrentThread(), // source (pseudo) handle
|
||||||
|
GetCurrentProcess(), // target process
|
||||||
|
&hCurrentThread, // target handle
|
||||||
|
0,
|
||||||
|
FALSE,
|
||||||
|
DUPLICATE_SAME_ACCESS);
|
||||||
|
assert(bRet);
|
||||||
|
if (!bRet) {
|
||||||
|
hCurrentThread = GetCurrentThread();
|
||||||
|
}
|
||||||
|
return hCurrentThread;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// 7.25.5.3
|
||||||
|
static inline int
|
||||||
|
thrd_detach(thrd_t thr)
|
||||||
|
{
|
||||||
|
CloseHandle(thr);
|
||||||
|
return thrd_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.5.4
|
||||||
|
static inline int
|
||||||
|
thrd_equal(thrd_t thr0, thrd_t thr1)
|
||||||
|
{
|
||||||
|
return GetThreadId(thr0) == GetThreadId(thr1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.5.5
|
||||||
|
static inline void
|
||||||
|
thrd_exit(int res)
|
||||||
|
{
|
||||||
|
impl_tss_dtor_invoke();
|
||||||
|
_endthreadex((unsigned)res);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.5.6
|
||||||
|
static inline int
|
||||||
|
thrd_join(thrd_t thr, int *res)
|
||||||
|
{
|
||||||
|
DWORD w, code;
|
||||||
|
w = WaitForSingleObject(thr, INFINITE);
|
||||||
|
if (w != WAIT_OBJECT_0)
|
||||||
|
return thrd_error;
|
||||||
|
if (res) {
|
||||||
|
if (!GetExitCodeThread(thr, &code)) {
|
||||||
|
CloseHandle(thr);
|
||||||
|
return thrd_error;
|
||||||
|
}
|
||||||
|
*res = (int)code;
|
||||||
|
}
|
||||||
|
CloseHandle(thr);
|
||||||
|
return thrd_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.5.7
|
||||||
|
static inline void
|
||||||
|
thrd_sleep(const struct timespec *time_point, struct timespec *remaining)
|
||||||
|
{
|
||||||
|
assert(time_point);
|
||||||
|
assert(!remaining); /* not implemented */
|
||||||
|
Sleep(impl_timespec2msec(time_point));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.5.8
|
||||||
|
static inline void
|
||||||
|
thrd_yield(void)
|
||||||
|
{
|
||||||
|
SwitchToThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*----------- 7.25.6 Thread-specific storage functions -----------*/
|
||||||
|
// 7.25.6.1
|
||||||
|
static inline int
|
||||||
|
tss_create(tss_t *key, tss_dtor_t dtor)
|
||||||
|
{
|
||||||
|
if (!key) return thrd_error;
|
||||||
|
*key = TlsAlloc();
|
||||||
|
if (dtor) {
|
||||||
|
if (impl_tss_dtor_register(*key, dtor)) {
|
||||||
|
TlsFree(*key);
|
||||||
|
return thrd_error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (*key != 0xFFFFFFFF) ? thrd_success : thrd_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.6.2
|
||||||
|
static inline void
|
||||||
|
tss_delete(tss_t key)
|
||||||
|
{
|
||||||
|
TlsFree(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.6.3
|
||||||
|
static inline void *
|
||||||
|
tss_get(tss_t key)
|
||||||
|
{
|
||||||
|
return TlsGetValue(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.25.6.4
|
||||||
|
static inline int
|
||||||
|
tss_set(tss_t key, void *val)
|
||||||
|
{
|
||||||
|
return TlsSetValue(key, val) ? thrd_success : thrd_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------- 7.25.7 Time functions --------------------*/
|
||||||
|
// 7.25.6.1
|
||||||
|
#ifndef HAVE_TIMESPEC_GET
|
||||||
|
static inline int
|
||||||
|
timespec_get(struct timespec *ts, int base)
|
||||||
|
{
|
||||||
|
if (!ts) return 0;
|
||||||
|
if (base == TIME_UTC) {
|
||||||
|
ts->tv_sec = time(NULL);
|
||||||
|
ts->tv_nsec = 0;
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
@ -0,0 +1,27 @@
|
|||||||
|
/* Copyright 2019 Intel Corporation */
|
||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
#include "no_extern_c.h"
|
||||||
|
|
||||||
|
#ifndef _C11_COMPAT_H_
|
||||||
|
#define _C11_COMPAT_H_
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
/* This is C++ code, not C */
|
||||||
|
#elif (__STDC_VERSION__ >= 201112L)
|
||||||
|
/* Already C11 */
|
||||||
|
#else
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* C11 static_assert() macro
|
||||||
|
* assert.h only defines that name for C11 and above
|
||||||
|
*/
|
||||||
|
#ifndef static_assert
|
||||||
|
#define static_assert _Static_assert
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* !C++ && !C11 */
|
||||||
|
|
||||||
|
#endif /* _C11_COMPAT_H_ */
|
@ -0,0 +1,183 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
*
|
||||||
|
* Copyright 2007-2013 VMware, Inc.
|
||||||
|
* All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sub license, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice (including the
|
||||||
|
* next paragraph) shall be included in all copies or substantial portions
|
||||||
|
* of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
|
||||||
|
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#include "no_extern_c.h"
|
||||||
|
|
||||||
|
#ifndef _C99_COMPAT_H_
|
||||||
|
#define _C99_COMPAT_H_
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MSVC hacks.
|
||||||
|
*/
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
|
||||||
|
# if _MSC_VER < 1900
|
||||||
|
# error "Microsoft Visual Studio 2015 or higher required"
|
||||||
|
# endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Visual Studio will complain if we define the `inline` keyword, but
|
||||||
|
* actually it only supports the keyword on C++.
|
||||||
|
*
|
||||||
|
* To avoid this the _ALLOW_KEYWORD_MACROS must be set.
|
||||||
|
*/
|
||||||
|
# if !defined(_ALLOW_KEYWORD_MACROS)
|
||||||
|
# define _ALLOW_KEYWORD_MACROS
|
||||||
|
# endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XXX: MSVC has a `__restrict` keyword, but it also has a
|
||||||
|
* `__declspec(restrict)` modifier, so it is impossible to define a
|
||||||
|
* `restrict` macro without interfering with the latter. Furthermore the
|
||||||
|
* MSVC standard library uses __declspec(restrict) under the _CRTRESTRICT
|
||||||
|
* macro. For now resolve this issue by redefining _CRTRESTRICT, but going
|
||||||
|
* forward we should probably should stop using restrict, especially
|
||||||
|
* considering that our code does not obbey strict aliasing rules any way.
|
||||||
|
*/
|
||||||
|
# include <crtdefs.h>
|
||||||
|
# undef _CRTRESTRICT
|
||||||
|
# define _CRTRESTRICT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* C99 inline keyword
|
||||||
|
*/
|
||||||
|
#ifndef inline
|
||||||
|
# ifdef __cplusplus
|
||||||
|
/* C++ supports inline keyword */
|
||||||
|
# elif defined(__GNUC__)
|
||||||
|
# define inline __inline__
|
||||||
|
# elif defined(_MSC_VER)
|
||||||
|
# define inline __inline
|
||||||
|
# elif defined(__ICL)
|
||||||
|
# define inline __inline
|
||||||
|
# elif defined(__INTEL_COMPILER)
|
||||||
|
/* Intel compiler supports inline keyword */
|
||||||
|
# elif defined(__WATCOMC__) && (__WATCOMC__ >= 1100)
|
||||||
|
# define inline __inline
|
||||||
|
# elif (__STDC_VERSION__ >= 199901L)
|
||||||
|
/* C99 supports inline keyword */
|
||||||
|
# else
|
||||||
|
# define inline
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* C99 restrict keyword
|
||||||
|
*
|
||||||
|
* See also:
|
||||||
|
* - http://cellperformance.beyond3d.com/articles/2006/05/demystifying-the-restrict-keyword.html
|
||||||
|
*/
|
||||||
|
#ifndef restrict
|
||||||
|
# if (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus)
|
||||||
|
/* C99 */
|
||||||
|
# elif defined(__GNUC__)
|
||||||
|
# define restrict __restrict__
|
||||||
|
# elif defined(_MSC_VER)
|
||||||
|
# define restrict __restrict
|
||||||
|
# else
|
||||||
|
# define restrict /* */
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* C99 __func__ macro
|
||||||
|
*/
|
||||||
|
#ifndef __func__
|
||||||
|
# if (__STDC_VERSION__ >= 199901L)
|
||||||
|
/* C99 */
|
||||||
|
# elif defined(__GNUC__)
|
||||||
|
# define __func__ __FUNCTION__
|
||||||
|
# elif defined(_MSC_VER)
|
||||||
|
# define __func__ __FUNCTION__
|
||||||
|
# else
|
||||||
|
# define __func__ "<unknown>"
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Simple test case for debugging */
|
||||||
|
#if 0
|
||||||
|
static inline const char *
|
||||||
|
test_c99_compat_h(const void * restrict a,
|
||||||
|
const void * restrict b)
|
||||||
|
{
|
||||||
|
return __func__;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Fallback definitions, for scons which doesn't auto-detect these things. */
|
||||||
|
#ifdef HAVE_SCONS
|
||||||
|
|
||||||
|
# ifndef _WIN32
|
||||||
|
# define HAVE_PTHREAD
|
||||||
|
# define HAVE_POSIX_MEMALIGN
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifdef __GNUC__
|
||||||
|
# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 2)
|
||||||
|
# error "GCC version 4.2 or higher required"
|
||||||
|
# endif
|
||||||
|
|
||||||
|
/* https://gcc.gnu.org/onlinedocs/gcc-4.2.4/gcc/Other-Builtins.html */
|
||||||
|
# define HAVE___BUILTIN_CLZ 1
|
||||||
|
# define HAVE___BUILTIN_CLZLL 1
|
||||||
|
# define HAVE___BUILTIN_CTZ 1
|
||||||
|
# define HAVE___BUILTIN_EXPECT 1
|
||||||
|
# define HAVE___BUILTIN_FFS 1
|
||||||
|
# define HAVE___BUILTIN_FFSLL 1
|
||||||
|
# define HAVE___BUILTIN_POPCOUNT 1
|
||||||
|
# define HAVE___BUILTIN_POPCOUNTLL 1
|
||||||
|
/* https://gcc.gnu.org/onlinedocs/gcc-4.2.4/gcc/Function-Attributes.html */
|
||||||
|
# define HAVE_FUNC_ATTRIBUTE_FLATTEN 1
|
||||||
|
# define HAVE_FUNC_ATTRIBUTE_UNUSED 1
|
||||||
|
# define HAVE_FUNC_ATTRIBUTE_FORMAT 1
|
||||||
|
# define HAVE_FUNC_ATTRIBUTE_PACKED 1
|
||||||
|
# define HAVE_FUNC_ATTRIBUTE_ALIAS 1
|
||||||
|
# define HAVE_FUNC_ATTRIBUTE_NORETURN 1
|
||||||
|
|
||||||
|
# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)
|
||||||
|
/* https://gcc.gnu.org/onlinedocs/gcc-4.3.6/gcc/Other-Builtins.html */
|
||||||
|
# define HAVE___BUILTIN_BSWAP32 1
|
||||||
|
# define HAVE___BUILTIN_BSWAP64 1
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
|
||||||
|
# define HAVE___BUILTIN_UNREACHABLE 1
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# endif /* __GNUC__ */
|
||||||
|
|
||||||
|
#endif /* HAVE_SCONS */
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _C99_COMPAT_H_ */
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2010 Valve Software
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice (including the next
|
||||||
|
* paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
* Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
* IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Code for fast 32-bit unsigned remainder, based off of "Faster Remainder by
|
||||||
|
* Direct Computation: Applications to Compilers and Software Libraries,"
|
||||||
|
* available at https://arxiv.org/pdf/1902.01961.pdf.
|
||||||
|
*
|
||||||
|
* util_fast_urem32(n, d, REMAINDER_MAGIC(d)) returns the same thing as
|
||||||
|
* n % d for any unsigned n and d, however it compiles down to only a few
|
||||||
|
* multiplications, so it should be faster than plain uint32_t modulo if the
|
||||||
|
* same divisor is used many times.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define REMAINDER_MAGIC(divisor) \
|
||||||
|
((uint64_t) ~0ull / (divisor) + 1)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get bits 64-96 of a 32x64-bit multiply. If __int128_t is available, we use
|
||||||
|
* it, which usually compiles down to one instruction on 64-bit architectures.
|
||||||
|
* Otherwise on 32-bit architectures we usually get four instructions (one
|
||||||
|
* 32x32->64 multiply, one 32x32->32 multiply, and one 64-bit add).
|
||||||
|
*/
|
||||||
|
|
||||||
|
static inline uint32_t
|
||||||
|
_mul32by64_hi(uint32_t a, uint64_t b)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_UINT128
|
||||||
|
return ((__uint128_t) b * a) >> 64;
|
||||||
|
#else
|
||||||
|
/*
|
||||||
|
* Let b = b0 + 2^32 * b1. Then a * b = a * b0 + 2^32 * a * b1. We would
|
||||||
|
* have to do a 96-bit addition to get the full result, except that only
|
||||||
|
* one term has non-zero lower 32 bits, which means that to get the high 32
|
||||||
|
* bits, we only have to add the high 64 bits of each term. Unfortunately,
|
||||||
|
* we have to do the 64-bit addition in case the low 32 bits overflow.
|
||||||
|
*/
|
||||||
|
uint32_t b0 = (uint32_t) b;
|
||||||
|
uint32_t b1 = b >> 32;
|
||||||
|
return ((((uint64_t) a * b0) >> 32) + (uint64_t) a * b1) >> 32;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t
|
||||||
|
util_fast_urem32(uint32_t n, uint32_t d, uint64_t magic)
|
||||||
|
{
|
||||||
|
uint64_t lowbits = magic * n;
|
||||||
|
uint32_t result = _mul32by64_hi(d, lowbits);
|
||||||
|
assert(result == n % d);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,425 @@
|
|||||||
|
/**
|
||||||
|
* \file hash.c
|
||||||
|
* Generic hash table.
|
||||||
|
*
|
||||||
|
* Used for display lists, texture objects, vertex/fragment programs,
|
||||||
|
* buffer objects, etc. The hash functions are thread-safe.
|
||||||
|
*
|
||||||
|
* \note key=0 is illegal.
|
||||||
|
*
|
||||||
|
* \author Brian Paul
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mesa 3-D graphics library
|
||||||
|
*
|
||||||
|
* Copyright (C) 1999-2006 Brian Paul All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
//#include "errors.h"
|
||||||
|
#include <GL/gl.h>
|
||||||
|
#include "hash.h"
|
||||||
|
#include "../util/hash_table.h"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new hash table.
|
||||||
|
*
|
||||||
|
* \return pointer to a new, empty hash table.
|
||||||
|
*/
|
||||||
|
struct _mesa_HashTable *
|
||||||
|
_mesa_NewHashTable(void)
|
||||||
|
{
|
||||||
|
struct _mesa_HashTable *table = CALLOC_STRUCT(_mesa_HashTable);
|
||||||
|
|
||||||
|
if (table) {
|
||||||
|
table->ht = _mesa_hash_table_create(NULL, uint_key_hash,
|
||||||
|
uint_key_compare);
|
||||||
|
if (table->ht == NULL) {
|
||||||
|
free(table);
|
||||||
|
//_mesa_error_no_memory(__func__);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
_mesa_hash_table_set_deleted_key(table->ht, uint_key(DELETED_KEY_VALUE));
|
||||||
|
/*
|
||||||
|
* Needs to be recursive, since the callback in _mesa_HashWalk()
|
||||||
|
* is allowed to call _mesa_HashRemove().
|
||||||
|
*/
|
||||||
|
mtx_init(&table->Mutex, mtx_recursive);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//_mesa_error_no_memory(__func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a hash table.
|
||||||
|
* Frees each entry on the hash table and then the hash table structure itself.
|
||||||
|
* Note that the caller should have already traversed the table and deleted
|
||||||
|
* the objects in the table (i.e. We don't free the entries' data pointer).
|
||||||
|
*
|
||||||
|
* \param table the hash table to delete.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
_mesa_DeleteHashTable(struct _mesa_HashTable *table)
|
||||||
|
{
|
||||||
|
assert(table);
|
||||||
|
|
||||||
|
if (_mesa_hash_table_next_entry(table->ht, NULL) != NULL) {
|
||||||
|
// _mesa_problem(NULL, "In _mesa_DeleteHashTable, found non-freed data");
|
||||||
|
}
|
||||||
|
|
||||||
|
_mesa_hash_table_destroy(table->ht, NULL);
|
||||||
|
|
||||||
|
mtx_destroy(&table->Mutex);
|
||||||
|
free(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup an entry in the hash table, without locking.
|
||||||
|
* \sa _mesa_HashLookup
|
||||||
|
*/
|
||||||
|
static inline void *
|
||||||
|
_mesa_HashLookup_unlocked(struct _mesa_HashTable *table, GLuint key)
|
||||||
|
{
|
||||||
|
const struct hash_entry *entry;
|
||||||
|
|
||||||
|
assert(table);
|
||||||
|
assert(key);
|
||||||
|
|
||||||
|
if (key == DELETED_KEY_VALUE)
|
||||||
|
return table->deleted_key_data;
|
||||||
|
|
||||||
|
entry = _mesa_hash_table_search_pre_hashed(table->ht,
|
||||||
|
uint_hash(key),
|
||||||
|
uint_key(key));
|
||||||
|
if (!entry)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return entry->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup an entry in the hash table.
|
||||||
|
*
|
||||||
|
* \param table the hash table.
|
||||||
|
* \param key the key.
|
||||||
|
*
|
||||||
|
* \return pointer to user's data or NULL if key not in table
|
||||||
|
*/
|
||||||
|
void *
|
||||||
|
_mesa_HashLookup(struct _mesa_HashTable *table, GLuint key)
|
||||||
|
{
|
||||||
|
void *res;
|
||||||
|
_mesa_HashLockMutex(table);
|
||||||
|
res = _mesa_HashLookup_unlocked(table, key);
|
||||||
|
_mesa_HashUnlockMutex(table);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup an entry in the hash table without locking the mutex.
|
||||||
|
*
|
||||||
|
* The hash table mutex must be locked manually by calling
|
||||||
|
* _mesa_HashLockMutex() before calling this function.
|
||||||
|
*
|
||||||
|
* \param table the hash table.
|
||||||
|
* \param key the key.
|
||||||
|
*
|
||||||
|
* \return pointer to user's data or NULL if key not in table
|
||||||
|
*/
|
||||||
|
void *
|
||||||
|
_mesa_HashLookupLocked(struct _mesa_HashTable *table, GLuint key)
|
||||||
|
{
|
||||||
|
return _mesa_HashLookup_unlocked(table, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
_mesa_HashInsert_unlocked(struct _mesa_HashTable *table, GLuint key, void *data)
|
||||||
|
{
|
||||||
|
uint32_t hash = uint_hash(key);
|
||||||
|
struct hash_entry *entry;
|
||||||
|
|
||||||
|
assert(table);
|
||||||
|
assert(key);
|
||||||
|
|
||||||
|
if (key > table->MaxKey)
|
||||||
|
table->MaxKey = key;
|
||||||
|
|
||||||
|
if (key == DELETED_KEY_VALUE) {
|
||||||
|
table->deleted_key_data = data;
|
||||||
|
} else {
|
||||||
|
entry = _mesa_hash_table_search_pre_hashed(table->ht, hash, uint_key(key));
|
||||||
|
if (entry) {
|
||||||
|
entry->data = data;
|
||||||
|
} else {
|
||||||
|
_mesa_hash_table_insert_pre_hashed(table->ht, hash, uint_key(key), data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert a key/pointer pair into the hash table without locking the mutex.
|
||||||
|
* If an entry with this key already exists we'll replace the existing entry.
|
||||||
|
*
|
||||||
|
* The hash table mutex must be locked manually by calling
|
||||||
|
* _mesa_HashLockMutex() before calling this function.
|
||||||
|
*
|
||||||
|
* \param table the hash table.
|
||||||
|
* \param key the key (not zero).
|
||||||
|
* \param data pointer to user data.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
_mesa_HashInsertLocked(struct _mesa_HashTable *table, GLuint key, void *data)
|
||||||
|
{
|
||||||
|
_mesa_HashInsert_unlocked(table, key, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert a key/pointer pair into the hash table.
|
||||||
|
* If an entry with this key already exists we'll replace the existing entry.
|
||||||
|
*
|
||||||
|
* \param table the hash table.
|
||||||
|
* \param key the key (not zero).
|
||||||
|
* \param data pointer to user data.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
_mesa_HashInsert(struct _mesa_HashTable *table, GLuint key, void *data)
|
||||||
|
{
|
||||||
|
_mesa_HashLockMutex(table);
|
||||||
|
_mesa_HashInsert_unlocked(table, key, data);
|
||||||
|
_mesa_HashUnlockMutex(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an entry from the hash table.
|
||||||
|
*
|
||||||
|
* \param table the hash table.
|
||||||
|
* \param key key of entry to remove.
|
||||||
|
*
|
||||||
|
* While holding the hash table's lock, searches the entry with the matching
|
||||||
|
* key and unlinks it.
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
_mesa_HashRemove_unlocked(struct _mesa_HashTable *table, GLuint key)
|
||||||
|
{
|
||||||
|
struct hash_entry *entry;
|
||||||
|
|
||||||
|
assert(table);
|
||||||
|
assert(key);
|
||||||
|
|
||||||
|
/* assert if _mesa_HashRemove illegally called from _mesa_HashDeleteAll
|
||||||
|
* callback function. Have to check this outside of mutex lock.
|
||||||
|
*/
|
||||||
|
assert(!table->InDeleteAll);
|
||||||
|
|
||||||
|
if (key == DELETED_KEY_VALUE) {
|
||||||
|
table->deleted_key_data = NULL;
|
||||||
|
} else {
|
||||||
|
entry = _mesa_hash_table_search_pre_hashed(table->ht,
|
||||||
|
uint_hash(key),
|
||||||
|
uint_key(key));
|
||||||
|
_mesa_hash_table_remove(table->ht, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
_mesa_HashRemoveLocked(struct _mesa_HashTable *table, GLuint key)
|
||||||
|
{
|
||||||
|
_mesa_HashRemove_unlocked(table, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_mesa_HashRemove(struct _mesa_HashTable *table, GLuint key)
|
||||||
|
{
|
||||||
|
_mesa_HashLockMutex(table);
|
||||||
|
_mesa_HashRemove_unlocked(table, key);
|
||||||
|
_mesa_HashUnlockMutex(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all entries in a hash table, but don't delete the table itself.
|
||||||
|
* Invoke the given callback function for each table entry.
|
||||||
|
*
|
||||||
|
* \param table the hash table to delete
|
||||||
|
* \param callback the callback function
|
||||||
|
* \param userData arbitrary pointer to pass along to the callback
|
||||||
|
* (this is typically a struct gl_context pointer)
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
_mesa_HashDeleteAll(struct _mesa_HashTable *table,
|
||||||
|
void (*callback)(GLuint key, void *data, void *userData),
|
||||||
|
void *userData)
|
||||||
|
{
|
||||||
|
assert(callback);
|
||||||
|
_mesa_HashLockMutex(table);
|
||||||
|
table->InDeleteAll = GL_TRUE;
|
||||||
|
hash_table_foreach(table->ht, entry) {
|
||||||
|
callback((uintptr_t)entry->key, entry->data, userData);
|
||||||
|
_mesa_hash_table_remove(table->ht, entry);
|
||||||
|
}
|
||||||
|
if (table->deleted_key_data) {
|
||||||
|
callback(DELETED_KEY_VALUE, table->deleted_key_data, userData);
|
||||||
|
table->deleted_key_data = NULL;
|
||||||
|
}
|
||||||
|
table->InDeleteAll = GL_FALSE;
|
||||||
|
_mesa_HashUnlockMutex(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Walk over all entries in a hash table, calling callback function for each.
|
||||||
|
* \param table the hash table to walk
|
||||||
|
* \param callback the callback function
|
||||||
|
* \param userData arbitrary pointer to pass along to the callback
|
||||||
|
* (this is typically a struct gl_context pointer)
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
hash_walk_unlocked(const struct _mesa_HashTable *table,
|
||||||
|
void (*callback)(GLuint key, void *data, void *userData),
|
||||||
|
void *userData)
|
||||||
|
{
|
||||||
|
assert(table);
|
||||||
|
assert(callback);
|
||||||
|
|
||||||
|
hash_table_foreach(table->ht, entry) {
|
||||||
|
callback((uintptr_t)entry->key, entry->data, userData);
|
||||||
|
}
|
||||||
|
if (table->deleted_key_data)
|
||||||
|
callback(DELETED_KEY_VALUE, table->deleted_key_data, userData);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
_mesa_HashWalk(const struct _mesa_HashTable *table,
|
||||||
|
void (*callback)(GLuint key, void *data, void *userData),
|
||||||
|
void *userData)
|
||||||
|
{
|
||||||
|
/* cast-away const */
|
||||||
|
struct _mesa_HashTable *table2 = (struct _mesa_HashTable *) table;
|
||||||
|
|
||||||
|
_mesa_HashLockMutex(table2);
|
||||||
|
hash_walk_unlocked(table, callback, userData);
|
||||||
|
_mesa_HashUnlockMutex(table2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_mesa_HashWalkLocked(const struct _mesa_HashTable *table,
|
||||||
|
void (*callback)(GLuint key, void *data, void *userData),
|
||||||
|
void *userData)
|
||||||
|
{
|
||||||
|
hash_walk_unlocked(table, callback, userData);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
debug_print_entry(GLuint key, void *data, void *userData)
|
||||||
|
{
|
||||||
|
//_mesa_debug(NULL, "%u %p\n", key, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump contents of hash table for debugging.
|
||||||
|
*
|
||||||
|
* \param table the hash table.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
_mesa_HashPrint(const struct _mesa_HashTable *table)
|
||||||
|
{
|
||||||
|
if (table->deleted_key_data)
|
||||||
|
debug_print_entry(DELETED_KEY_VALUE, table->deleted_key_data, NULL);
|
||||||
|
_mesa_HashWalk(table, debug_print_entry, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a block of adjacent unused hash keys.
|
||||||
|
*
|
||||||
|
* \param table the hash table.
|
||||||
|
* \param numKeys number of keys needed.
|
||||||
|
*
|
||||||
|
* \return Starting key of free block or 0 if failure.
|
||||||
|
*
|
||||||
|
* If there are enough free keys between the maximum key existing in the table
|
||||||
|
* (_mesa_HashTable::MaxKey) and the maximum key possible, then simply return
|
||||||
|
* the adjacent key. Otherwise do a full search for a free key block in the
|
||||||
|
* allowable key range.
|
||||||
|
*/
|
||||||
|
GLuint
|
||||||
|
_mesa_HashFindFreeKeyBlock(struct _mesa_HashTable *table, GLuint numKeys)
|
||||||
|
{
|
||||||
|
const GLuint maxKey = ~((GLuint) 0) - 1;
|
||||||
|
if (maxKey - numKeys > table->MaxKey) {
|
||||||
|
/* the quick solution */
|
||||||
|
return table->MaxKey + 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* the slow solution */
|
||||||
|
GLuint freeCount = 0;
|
||||||
|
GLuint freeStart = 1;
|
||||||
|
GLuint key;
|
||||||
|
for (key = 1; key != maxKey; key++) {
|
||||||
|
if (_mesa_HashLookup_unlocked(table, key)) {
|
||||||
|
/* darn, this key is already in use */
|
||||||
|
freeCount = 0;
|
||||||
|
freeStart = key+1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* this key not in use, check if we've found enough */
|
||||||
|
freeCount++;
|
||||||
|
if (freeCount == numKeys) {
|
||||||
|
return freeStart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* cannot allocate a block of numKeys consecutive keys */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the number of entries in the hash table.
|
||||||
|
*/
|
||||||
|
GLuint
|
||||||
|
_mesa_HashNumEntries(const struct _mesa_HashTable *table)
|
||||||
|
{
|
||||||
|
GLuint count = 0;
|
||||||
|
|
||||||
|
if (table->deleted_key_data)
|
||||||
|
count++;
|
||||||
|
|
||||||
|
count += _mesa_hash_table_num_entries(table->ht);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
@ -0,0 +1,191 @@
|
|||||||
|
/**
|
||||||
|
* \file hash.h
|
||||||
|
* Generic hash table.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mesa 3-D graphics library
|
||||||
|
*
|
||||||
|
* Copyright (C) 1999-2006 Brian Paul All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef HASH_H
|
||||||
|
#define HASH_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <GL/gl.h>
|
||||||
|
//#include "imports.h"
|
||||||
|
#include "../c11/threads.h"
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
/** Memory macros */
|
||||||
|
/*@{*/
|
||||||
|
|
||||||
|
/** Allocate a structure of type \p T */
|
||||||
|
#define MALLOC_STRUCT(T) (struct T *) malloc(sizeof(struct T))
|
||||||
|
/** Allocate and zero a structure of type \p T */
|
||||||
|
#define CALLOC_STRUCT(T) (struct T *) calloc(1, sizeof(struct T))
|
||||||
|
|
||||||
|
/*@}*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Magic GLuint object name that gets stored outside of the struct hash_table.
|
||||||
|
*
|
||||||
|
* The hash table needs a particular pointer to be the marker for a key that
|
||||||
|
* was deleted from the table, along with NULL for the "never allocated in the
|
||||||
|
* table" marker. Legacy GL allows any GLuint to be used as a GL object name,
|
||||||
|
* and we use a 1:1 mapping from GLuints to key pointers, so we need to be
|
||||||
|
* able to track a GLuint that happens to match the deleted key outside of
|
||||||
|
* struct hash_table. We tell the hash table to use "1" as the deleted key
|
||||||
|
* value, so that we test the deleted-key-in-the-table path as best we can.
|
||||||
|
*/
|
||||||
|
#define DELETED_KEY_VALUE 1
|
||||||
|
|
||||||
|
/** @{
|
||||||
|
* Mapping from our use of GLuint as both the key and the hash value to the
|
||||||
|
* hash_table.h API
|
||||||
|
*
|
||||||
|
* There exist many integer hash functions, designed to avoid collisions when
|
||||||
|
* the integers are spread across key space with some patterns. In GL, the
|
||||||
|
* pattern (in the case of glGen*()ed object IDs) is that the keys are unique
|
||||||
|
* contiguous integers starting from 1. Because of that, we just use the key
|
||||||
|
* as the hash value, to minimize the cost of the hash function. If objects
|
||||||
|
* are never deleted, we will never see a collision in the table, because the
|
||||||
|
* table resizes itself when it approaches full, and thus key % table_size ==
|
||||||
|
* key.
|
||||||
|
*
|
||||||
|
* The case where we could have collisions for genned objects would be
|
||||||
|
* something like: glGenBuffers(&a, 100); glDeleteBuffers(&a + 50, 50);
|
||||||
|
* glGenBuffers(&b, 100), because objects 1-50 and 101-200 are allocated at
|
||||||
|
* the end of that sequence, instead of 1-150. So far it doesn't appear to be
|
||||||
|
* a problem.
|
||||||
|
*/
|
||||||
|
static inline bool
|
||||||
|
uint_key_compare(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
return a == b;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t
|
||||||
|
uint_hash(GLuint id)
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t
|
||||||
|
uint_key_hash(const void *key)
|
||||||
|
{
|
||||||
|
return uint_hash((uintptr_t)key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void *
|
||||||
|
uint_key(GLuint id)
|
||||||
|
{
|
||||||
|
return (void *)(uintptr_t) id;
|
||||||
|
}
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The hash table data structure.
|
||||||
|
*/
|
||||||
|
struct _mesa_HashTable {
|
||||||
|
struct hash_table *ht;
|
||||||
|
GLuint MaxKey; /**< highest key inserted so far */
|
||||||
|
mtx_t Mutex; /**< mutual exclusion lock */
|
||||||
|
GLboolean InDeleteAll; /**< Debug check */
|
||||||
|
/** Value that would be in the table for DELETED_KEY_VALUE. */
|
||||||
|
void *deleted_key_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct _mesa_HashTable *_mesa_NewHashTable(void);
|
||||||
|
|
||||||
|
extern void _mesa_DeleteHashTable(struct _mesa_HashTable *table);
|
||||||
|
|
||||||
|
extern void *_mesa_HashLookup(struct _mesa_HashTable *table, GLuint key);
|
||||||
|
|
||||||
|
extern void _mesa_HashInsert(struct _mesa_HashTable *table, GLuint key, void *data);
|
||||||
|
|
||||||
|
extern void _mesa_HashRemove(struct _mesa_HashTable *table, GLuint key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lock the hash table mutex.
|
||||||
|
*
|
||||||
|
* This function should be used when multiple objects need
|
||||||
|
* to be looked up in the hash table, to avoid having to lock
|
||||||
|
* and unlock the mutex each time.
|
||||||
|
*
|
||||||
|
* \param table the hash table.
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
_mesa_HashLockMutex(struct _mesa_HashTable *table)
|
||||||
|
{
|
||||||
|
assert(table);
|
||||||
|
mtx_lock(&table->Mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlock the hash table mutex.
|
||||||
|
*
|
||||||
|
* \param table the hash table.
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
_mesa_HashUnlockMutex(struct _mesa_HashTable *table)
|
||||||
|
{
|
||||||
|
assert(table);
|
||||||
|
mtx_unlock(&table->Mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void *_mesa_HashLookupLocked(struct _mesa_HashTable *table, GLuint key);
|
||||||
|
|
||||||
|
extern void _mesa_HashInsertLocked(struct _mesa_HashTable *table,
|
||||||
|
GLuint key, void *data);
|
||||||
|
|
||||||
|
extern void _mesa_HashRemoveLocked(struct _mesa_HashTable *table, GLuint key);
|
||||||
|
|
||||||
|
extern void
|
||||||
|
_mesa_HashDeleteAll(struct _mesa_HashTable *table,
|
||||||
|
void (*callback)(GLuint key, void *data, void *userData),
|
||||||
|
void *userData);
|
||||||
|
|
||||||
|
extern void
|
||||||
|
_mesa_HashWalk(const struct _mesa_HashTable *table,
|
||||||
|
void (*callback)(GLuint key, void *data, void *userData),
|
||||||
|
void *userData);
|
||||||
|
|
||||||
|
extern void
|
||||||
|
_mesa_HashWalkLocked(const struct _mesa_HashTable *table,
|
||||||
|
void (*callback)(GLuint key, void *data, void *userData),
|
||||||
|
void *userData);
|
||||||
|
|
||||||
|
extern void _mesa_HashPrint(const struct _mesa_HashTable *table);
|
||||||
|
|
||||||
|
extern GLuint _mesa_HashFindFreeKeyBlock(struct _mesa_HashTable *table, GLuint numKeys);
|
||||||
|
|
||||||
|
extern GLuint
|
||||||
|
_mesa_HashNumEntries(const struct _mesa_HashTable *table);
|
||||||
|
|
||||||
|
extern void _mesa_test_hash_functions(void);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,48 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
*
|
||||||
|
* Copyright 2014 VMware, Inc.
|
||||||
|
* All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Including system's headers inside `extern "C" { ... }` is not safe, as system
|
||||||
|
* headers may have C++ code in them, and C++ code inside extern "C"
|
||||||
|
* leads to syntatically incorrect code.
|
||||||
|
*
|
||||||
|
* This is because putting code inside extern "C" won't make __cplusplus define
|
||||||
|
* go away, that is, the system header being included thinks is free to use C++
|
||||||
|
* as it sees fits.
|
||||||
|
*
|
||||||
|
* Including non-system headers inside extern "C" is not safe either, because
|
||||||
|
* non-system headers end up including system headers, hence fall in the above
|
||||||
|
* case too.
|
||||||
|
*
|
||||||
|
* Conclusion, includes inside extern "C" is simply not portable.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This header helps surface these issues.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
template<class T> class _IncludeInsideExternCNotPortable;
|
||||||
|
#endif
|
@ -0,0 +1,131 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
/* Copyright 2008 VMware, Inc. */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-detect the operating system family.
|
||||||
|
*
|
||||||
|
* See also:
|
||||||
|
* - http://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
|
||||||
|
* - echo | gcc -dM -E - | sort
|
||||||
|
* - http://msdn.microsoft.com/en-us/library/b0084kay.aspx
|
||||||
|
*
|
||||||
|
* @author José Fonseca <jfonseca@vmware.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DETECT_OS_H
|
||||||
|
#define DETECT_OS_H
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
#define DETECT_OS_LINUX 1
|
||||||
|
#define DETECT_OS_UNIX 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Android defines __linux__, so DETECT_OS_LINUX and DETECT_OS_UNIX will
|
||||||
|
* also be defined.
|
||||||
|
*/
|
||||||
|
#if defined(ANDROID)
|
||||||
|
#define DETECT_OS_ANDROID 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||||
|
#define DETECT_OS_FREEBSD 1
|
||||||
|
#define DETECT_OS_BSD 1
|
||||||
|
#define DETECT_OS_UNIX 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__OpenBSD__)
|
||||||
|
#define DETECT_OS_OPENBSD 1
|
||||||
|
#define DETECT_OS_BSD 1
|
||||||
|
#define DETECT_OS_UNIX 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__NetBSD__)
|
||||||
|
#define DETECT_OS_NETBSD 1
|
||||||
|
#define DETECT_OS_BSD 1
|
||||||
|
#define DETECT_OS_UNIX 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__DragonFly__)
|
||||||
|
#define DETECT_OS_DRAGONFLY 1
|
||||||
|
#define DETECT_OS_BSD 1
|
||||||
|
#define DETECT_OS_UNIX 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__GNU__)
|
||||||
|
#define DETECT_OS_HURD 1
|
||||||
|
#define DETECT_OS_UNIX 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__sun)
|
||||||
|
#define DETECT_OS_SOLARIS 1
|
||||||
|
#define DETECT_OS_UNIX 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
#define DETECT_OS_APPLE 1
|
||||||
|
#define DETECT_OS_UNIX 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN32) || defined(WIN32)
|
||||||
|
#define DETECT_OS_WINDOWS 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__HAIKU__)
|
||||||
|
#define DETECT_OS_HAIKU 1
|
||||||
|
#define DETECT_OS_UNIX 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__CYGWIN__)
|
||||||
|
#define DETECT_OS_CYGWIN 1
|
||||||
|
#define DETECT_OS_UNIX 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure DETECT_OS_* are always defined, so that they can be used with #if
|
||||||
|
*/
|
||||||
|
#ifndef DETECT_OS_ANDROID
|
||||||
|
#define DETECT_OS_ANDROID 0
|
||||||
|
#endif
|
||||||
|
#ifndef DETECT_OS_APPLE
|
||||||
|
#define DETECT_OS_APPLE 0
|
||||||
|
#endif
|
||||||
|
#ifndef DETECT_OS_BSD
|
||||||
|
#define DETECT_OS_BSD 0
|
||||||
|
#endif
|
||||||
|
#ifndef DETECT_OS_CYGWIN
|
||||||
|
#define DETECT_OS_CYGWIN 0
|
||||||
|
#endif
|
||||||
|
#ifndef DETECT_OS_DRAGONFLY
|
||||||
|
#define DETECT_OS_DRAGONFLY 0
|
||||||
|
#endif
|
||||||
|
#ifndef DETECT_OS_FREEBSD
|
||||||
|
#define DETECT_OS_FREEBSD 0
|
||||||
|
#endif
|
||||||
|
#ifndef DETECT_OS_HAIKU
|
||||||
|
#define DETECT_OS_HAIKU 0
|
||||||
|
#endif
|
||||||
|
#ifndef DETECT_OS_HURD
|
||||||
|
#define DETECT_OS_HURD 0
|
||||||
|
#endif
|
||||||
|
#ifndef DETECT_OS_LINUX
|
||||||
|
#define DETECT_OS_LINUX 0
|
||||||
|
#endif
|
||||||
|
#ifndef DETECT_OS_NETBSD
|
||||||
|
#define DETECT_OS_NETBSD 0
|
||||||
|
#endif
|
||||||
|
#ifndef DETECT_OS_OPENBSD
|
||||||
|
#define DETECT_OS_OPENBSD 0
|
||||||
|
#endif
|
||||||
|
#ifndef DETECT_OS_SOLARIS
|
||||||
|
#define DETECT_OS_SOLARIS 0
|
||||||
|
#endif
|
||||||
|
#ifndef DETECT_OS_UNIX
|
||||||
|
#define DETECT_OS_UNIX 0
|
||||||
|
#endif
|
||||||
|
#ifndef DETECT_OS_WINDOWS
|
||||||
|
#define DETECT_OS_WINDOWS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* DETECT_OS_H */
|
@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2015 Intel
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice (including the next
|
||||||
|
* paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
* Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
* IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef UTIL_FUTEX_H
|
||||||
|
#define UTIL_FUTEX_H
|
||||||
|
|
||||||
|
#if defined(HAVE_LINUX_FUTEX_H)
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <linux/futex.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
static inline long sys_futex(void *addr1, int op, int val1, const struct timespec *timeout, void *addr2, int val3)
|
||||||
|
{
|
||||||
|
return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int futex_wake(uint32_t *addr, int count)
|
||||||
|
{
|
||||||
|
return sys_futex(addr, FUTEX_WAKE, count, NULL, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int futex_wait(uint32_t *addr, int32_t value, const struct timespec *timeout)
|
||||||
|
{
|
||||||
|
/* FUTEX_WAIT_BITSET with FUTEX_BITSET_MATCH_ANY is equivalent to
|
||||||
|
* FUTEX_WAIT, except that it treats the timeout as absolute. */
|
||||||
|
return sys_futex(addr, FUTEX_WAIT_BITSET, value, timeout, NULL,
|
||||||
|
FUTEX_BITSET_MATCH_ANY);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__FreeBSD__)
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/umtx.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
static inline int futex_wake(uint32_t *addr, int count)
|
||||||
|
{
|
||||||
|
assert(count == (int)(uint32_t)count); /* Check that bits weren't discarded */
|
||||||
|
return _umtx_op(addr, UMTX_OP_WAKE, (uint32_t)count, NULL, NULL) == -1 ? errno : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int futex_wait(uint32_t *addr, int32_t value, struct timespec *timeout)
|
||||||
|
{
|
||||||
|
void *uaddr = NULL, *uaddr2 = NULL;
|
||||||
|
struct _umtx_time tmo = {
|
||||||
|
._flags = UMTX_ABSTIME,
|
||||||
|
._clockid = CLOCK_MONOTONIC
|
||||||
|
};
|
||||||
|
|
||||||
|
assert(value == (int)(uint32_t)value); /* Check that bits weren't discarded */
|
||||||
|
|
||||||
|
if (timeout != NULL) {
|
||||||
|
tmo._timeout = *timeout;
|
||||||
|
uaddr = (void *)(uintptr_t)sizeof(tmo);
|
||||||
|
uaddr2 = (void *)&tmo;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _umtx_op(addr, UMTX_OP_WAIT_UINT, (uint32_t)value, uaddr, uaddr2) == -1 ? errno : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__OpenBSD__)
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/futex.h>
|
||||||
|
|
||||||
|
static inline int futex_wake(uint32_t *addr, int count)
|
||||||
|
{
|
||||||
|
return futex(addr, FUTEX_WAKE, count, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int futex_wait(uint32_t *addr, int32_t value, const struct timespec *timeout)
|
||||||
|
{
|
||||||
|
struct timespec tsrel, tsnow;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &tsnow);
|
||||||
|
timespecsub(timeout, &tsrel, &tsrel);
|
||||||
|
return futex(addr, FUTEX_WAIT, value, &tsrel, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* UTIL_FUTEX_H */
|
@ -0,0 +1,802 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2009,2012 Intel Corporation
|
||||||
|
* Copyright © 1988-2004 Keith Packard and Bart Massey.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice (including the next
|
||||||
|
* paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
* Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
* IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* Except as contained in this notice, the names of the authors
|
||||||
|
* or their institutions shall not be used in advertising or
|
||||||
|
* otherwise to promote the sale, use or other dealings in this
|
||||||
|
* Software without prior written authorization from the
|
||||||
|
* authors.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Eric Anholt <eric@anholt.net>
|
||||||
|
* Keith Packard <keithp@keithp.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements an open-addressing, linear-reprobing hash table.
|
||||||
|
*
|
||||||
|
* For more information, see:
|
||||||
|
*
|
||||||
|
* http://cgit.freedesktop.org/~anholt/hash_table/tree/README
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "hash_table.h"
|
||||||
|
#include "ralloc.h"
|
||||||
|
#include "macros.h"
|
||||||
|
#include "../main/hash.h"
|
||||||
|
#include "../fast_urem_by_const.h"
|
||||||
|
|
||||||
|
static const uint32_t deleted_key_value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* From Knuth -- a good choice for hash/rehash values is p, p-2 where
|
||||||
|
* p and p-2 are both prime. These tables are sized to have an extra 10%
|
||||||
|
* free to avoid exponential performance degradation as the hash table fills
|
||||||
|
*/
|
||||||
|
static const struct {
|
||||||
|
uint32_t max_entries, size, rehash;
|
||||||
|
uint64_t size_magic, rehash_magic;
|
||||||
|
} hash_sizes[] = {
|
||||||
|
#define ENTRY(max_entries, size, rehash) \
|
||||||
|
{ max_entries, size, rehash, \
|
||||||
|
REMAINDER_MAGIC(size), REMAINDER_MAGIC(rehash) }
|
||||||
|
|
||||||
|
ENTRY(2, 5, 3 ),
|
||||||
|
ENTRY(4, 7, 5 ),
|
||||||
|
ENTRY(8, 13, 11 ),
|
||||||
|
ENTRY(16, 19, 17 ),
|
||||||
|
ENTRY(32, 43, 41 ),
|
||||||
|
ENTRY(64, 73, 71 ),
|
||||||
|
ENTRY(128, 151, 149 ),
|
||||||
|
ENTRY(256, 283, 281 ),
|
||||||
|
ENTRY(512, 571, 569 ),
|
||||||
|
ENTRY(1024, 1153, 1151 ),
|
||||||
|
ENTRY(2048, 2269, 2267 ),
|
||||||
|
ENTRY(4096, 4519, 4517 ),
|
||||||
|
ENTRY(8192, 9013, 9011 ),
|
||||||
|
ENTRY(16384, 18043, 18041 ),
|
||||||
|
ENTRY(32768, 36109, 36107 ),
|
||||||
|
ENTRY(65536, 72091, 72089 ),
|
||||||
|
ENTRY(131072, 144409, 144407 ),
|
||||||
|
ENTRY(262144, 288361, 288359 ),
|
||||||
|
ENTRY(524288, 576883, 576881 ),
|
||||||
|
ENTRY(1048576, 1153459, 1153457 ),
|
||||||
|
ENTRY(2097152, 2307163, 2307161 ),
|
||||||
|
ENTRY(4194304, 4613893, 4613891 ),
|
||||||
|
ENTRY(8388608, 9227641, 9227639 ),
|
||||||
|
ENTRY(16777216, 18455029, 18455027 ),
|
||||||
|
ENTRY(33554432, 36911011, 36911009 ),
|
||||||
|
ENTRY(67108864, 73819861, 73819859 ),
|
||||||
|
ENTRY(134217728, 147639589, 147639587 ),
|
||||||
|
ENTRY(268435456, 295279081, 295279079 ),
|
||||||
|
ENTRY(536870912, 590559793, 590559791 ),
|
||||||
|
ENTRY(1073741824, 1181116273, 1181116271 ),
|
||||||
|
ENTRY(2147483648ul, 2362232233ul, 2362232231ul )
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
key_pointer_is_reserved(const struct hash_table *ht, const void *key)
|
||||||
|
{
|
||||||
|
return key == NULL || key == ht->deleted_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
entry_is_free(const struct hash_entry *entry)
|
||||||
|
{
|
||||||
|
return entry->key == NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
entry_is_deleted(const struct hash_table *ht, struct hash_entry *entry)
|
||||||
|
{
|
||||||
|
return entry->key == ht->deleted_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
entry_is_present(const struct hash_table *ht, struct hash_entry *entry)
|
||||||
|
{
|
||||||
|
return entry->key != NULL && entry->key != ht->deleted_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
_mesa_hash_table_init(struct hash_table *ht,
|
||||||
|
void *mem_ctx,
|
||||||
|
uint32_t (*key_hash_function)(const void *key),
|
||||||
|
bool (*key_equals_function)(const void *a,
|
||||||
|
const void *b))
|
||||||
|
{
|
||||||
|
ht->size_index = 0;
|
||||||
|
ht->size = hash_sizes[ht->size_index].size;
|
||||||
|
ht->rehash = hash_sizes[ht->size_index].rehash;
|
||||||
|
ht->size_magic = hash_sizes[ht->size_index].size_magic;
|
||||||
|
ht->rehash_magic = hash_sizes[ht->size_index].rehash_magic;
|
||||||
|
ht->max_entries = hash_sizes[ht->size_index].max_entries;
|
||||||
|
ht->key_hash_function = key_hash_function;
|
||||||
|
ht->key_equals_function = key_equals_function;
|
||||||
|
ht->table = rzalloc_array(mem_ctx, struct hash_entry, ht->size);
|
||||||
|
ht->entries = 0;
|
||||||
|
ht->deleted_entries = 0;
|
||||||
|
ht->deleted_key = &deleted_key_value;
|
||||||
|
|
||||||
|
return ht->table != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct hash_table *
|
||||||
|
_mesa_hash_table_create(void *mem_ctx,
|
||||||
|
uint32_t (*key_hash_function)(const void *key),
|
||||||
|
bool (*key_equals_function)(const void *a,
|
||||||
|
const void *b))
|
||||||
|
{
|
||||||
|
struct hash_table *ht;
|
||||||
|
|
||||||
|
/* mem_ctx is used to allocate the hash table, but the hash table is used
|
||||||
|
* to allocate all of the suballocations.
|
||||||
|
*/
|
||||||
|
ht = ralloc(mem_ctx, struct hash_table);
|
||||||
|
if (ht == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!_mesa_hash_table_init(ht, ht, key_hash_function, key_equals_function)) {
|
||||||
|
ralloc_free(ht);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ht;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct hash_table *
|
||||||
|
_mesa_hash_table_clone(struct hash_table *src, void *dst_mem_ctx)
|
||||||
|
{
|
||||||
|
struct hash_table *ht;
|
||||||
|
|
||||||
|
ht = ralloc(dst_mem_ctx, struct hash_table);
|
||||||
|
if (ht == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
memcpy(ht, src, sizeof(struct hash_table));
|
||||||
|
|
||||||
|
ht->table = ralloc_array(ht, struct hash_entry, ht->size);
|
||||||
|
if (ht->table == NULL) {
|
||||||
|
ralloc_free(ht);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(ht->table, src->table, ht->size * sizeof(struct hash_entry));
|
||||||
|
|
||||||
|
return ht;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the given hash table.
|
||||||
|
*
|
||||||
|
* If delete_function is passed, it gets called on each entry present before
|
||||||
|
* freeing.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
_mesa_hash_table_destroy(struct hash_table *ht,
|
||||||
|
void (*delete_function)(struct hash_entry *entry))
|
||||||
|
{
|
||||||
|
if (!ht)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (delete_function) {
|
||||||
|
hash_table_foreach(ht, entry) {
|
||||||
|
delete_function(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ralloc_free(ht);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all entries of the given hash table without deleting the table
|
||||||
|
* itself or changing its structure.
|
||||||
|
*
|
||||||
|
* If delete_function is passed, it gets called on each entry present.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
_mesa_hash_table_clear(struct hash_table *ht,
|
||||||
|
void (*delete_function)(struct hash_entry *entry))
|
||||||
|
{
|
||||||
|
struct hash_entry *entry;
|
||||||
|
|
||||||
|
for (entry = ht->table; entry != ht->table + ht->size; entry++) {
|
||||||
|
if (entry->key == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (delete_function != NULL && entry->key != ht->deleted_key)
|
||||||
|
delete_function(entry);
|
||||||
|
|
||||||
|
entry->key = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ht->entries = 0;
|
||||||
|
ht->deleted_entries = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets the value of the key pointer used for deleted entries in the table.
|
||||||
|
*
|
||||||
|
* The assumption is that usually keys are actual pointers, so we use a
|
||||||
|
* default value of a pointer to an arbitrary piece of storage in the library.
|
||||||
|
* But in some cases a consumer wants to store some other sort of value in the
|
||||||
|
* table, like a uint32_t, in which case that pointer may conflict with one of
|
||||||
|
* their valid keys. This lets that user select a safe value.
|
||||||
|
*
|
||||||
|
* This must be called before any keys are actually deleted from the table.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
_mesa_hash_table_set_deleted_key(struct hash_table *ht, const void *deleted_key)
|
||||||
|
{
|
||||||
|
ht->deleted_key = deleted_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct hash_entry *
|
||||||
|
hash_table_search(struct hash_table *ht, uint32_t hash, const void *key)
|
||||||
|
{
|
||||||
|
assert(!key_pointer_is_reserved(ht, key));
|
||||||
|
|
||||||
|
uint32_t size = ht->size;
|
||||||
|
uint32_t start_hash_address = util_fast_urem32(hash, size, ht->size_magic);
|
||||||
|
uint32_t double_hash = 1 + util_fast_urem32(hash, ht->rehash,
|
||||||
|
ht->rehash_magic);
|
||||||
|
uint32_t hash_address = start_hash_address;
|
||||||
|
|
||||||
|
do {
|
||||||
|
struct hash_entry *entry = ht->table + hash_address;
|
||||||
|
|
||||||
|
if (entry_is_free(entry)) {
|
||||||
|
return NULL;
|
||||||
|
} else if (entry_is_present(ht, entry) && entry->hash == hash) {
|
||||||
|
if (ht->key_equals_function(key, entry->key)) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_address += double_hash;
|
||||||
|
if (hash_address >= size)
|
||||||
|
hash_address -= size;
|
||||||
|
} while (hash_address != start_hash_address);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a hash table entry with the given key and hash of that key.
|
||||||
|
*
|
||||||
|
* Returns NULL if no entry is found. Note that the data pointer may be
|
||||||
|
* modified by the user.
|
||||||
|
*/
|
||||||
|
struct hash_entry *
|
||||||
|
_mesa_hash_table_search(struct hash_table *ht, const void *key)
|
||||||
|
{
|
||||||
|
assert(ht->key_hash_function);
|
||||||
|
return hash_table_search(ht, ht->key_hash_function(key), key);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct hash_entry *
|
||||||
|
_mesa_hash_table_search_pre_hashed(struct hash_table *ht, uint32_t hash,
|
||||||
|
const void *key)
|
||||||
|
{
|
||||||
|
assert(ht->key_hash_function == NULL || hash == ht->key_hash_function(key));
|
||||||
|
return hash_table_search(ht, hash, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct hash_entry *
|
||||||
|
hash_table_insert(struct hash_table *ht, uint32_t hash,
|
||||||
|
const void *key, void *data);
|
||||||
|
|
||||||
|
static void
|
||||||
|
hash_table_insert_rehash(struct hash_table *ht, uint32_t hash,
|
||||||
|
const void *key, void *data)
|
||||||
|
{
|
||||||
|
uint32_t size = ht->size;
|
||||||
|
uint32_t start_hash_address = util_fast_urem32(hash, size, ht->size_magic);
|
||||||
|
uint32_t double_hash = 1 + util_fast_urem32(hash, ht->rehash,
|
||||||
|
ht->rehash_magic);
|
||||||
|
uint32_t hash_address = start_hash_address;
|
||||||
|
do {
|
||||||
|
struct hash_entry *entry = ht->table + hash_address;
|
||||||
|
|
||||||
|
if (likely(entry->key == NULL)) {
|
||||||
|
entry->hash = hash;
|
||||||
|
entry->key = key;
|
||||||
|
entry->data = data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_address += double_hash;
|
||||||
|
if (hash_address >= size)
|
||||||
|
hash_address -= size;
|
||||||
|
} while (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_mesa_hash_table_rehash(struct hash_table *ht, unsigned new_size_index)
|
||||||
|
{
|
||||||
|
struct hash_table old_ht;
|
||||||
|
struct hash_entry *table;
|
||||||
|
|
||||||
|
if (new_size_index >= ARRAY_SIZE(hash_sizes))
|
||||||
|
return;
|
||||||
|
|
||||||
|
table = rzalloc_array(ralloc_parent(ht->table), struct hash_entry,
|
||||||
|
hash_sizes[new_size_index].size);
|
||||||
|
if (table == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
old_ht = *ht;
|
||||||
|
|
||||||
|
ht->table = table;
|
||||||
|
ht->size_index = new_size_index;
|
||||||
|
ht->size = hash_sizes[ht->size_index].size;
|
||||||
|
ht->rehash = hash_sizes[ht->size_index].rehash;
|
||||||
|
ht->size_magic = hash_sizes[ht->size_index].size_magic;
|
||||||
|
ht->rehash_magic = hash_sizes[ht->size_index].rehash_magic;
|
||||||
|
ht->max_entries = hash_sizes[ht->size_index].max_entries;
|
||||||
|
ht->entries = 0;
|
||||||
|
ht->deleted_entries = 0;
|
||||||
|
|
||||||
|
hash_table_foreach(&old_ht, entry) {
|
||||||
|
hash_table_insert_rehash(ht, entry->hash, entry->key, entry->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
ht->entries = old_ht.entries;
|
||||||
|
|
||||||
|
ralloc_free(old_ht.table);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct hash_entry *
|
||||||
|
hash_table_insert(struct hash_table *ht, uint32_t hash,
|
||||||
|
const void *key, void *data)
|
||||||
|
{
|
||||||
|
struct hash_entry *available_entry = NULL;
|
||||||
|
|
||||||
|
assert(!key_pointer_is_reserved(ht, key));
|
||||||
|
|
||||||
|
if (ht->entries >= ht->max_entries) {
|
||||||
|
_mesa_hash_table_rehash(ht, ht->size_index + 1);
|
||||||
|
} else if (ht->deleted_entries + ht->entries >= ht->max_entries) {
|
||||||
|
_mesa_hash_table_rehash(ht, ht->size_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t size = ht->size;
|
||||||
|
uint32_t start_hash_address = util_fast_urem32(hash, size, ht->size_magic);
|
||||||
|
uint32_t double_hash = 1 + util_fast_urem32(hash, ht->rehash,
|
||||||
|
ht->rehash_magic);
|
||||||
|
uint32_t hash_address = start_hash_address;
|
||||||
|
do {
|
||||||
|
struct hash_entry *entry = ht->table + hash_address;
|
||||||
|
|
||||||
|
if (!entry_is_present(ht, entry)) {
|
||||||
|
/* Stash the first available entry we find */
|
||||||
|
if (available_entry == NULL)
|
||||||
|
available_entry = entry;
|
||||||
|
if (entry_is_free(entry))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Implement replacement when another insert happens
|
||||||
|
* with a matching key. This is a relatively common
|
||||||
|
* feature of hash tables, with the alternative
|
||||||
|
* generally being "insert the new value as well, and
|
||||||
|
* return it first when the key is searched for".
|
||||||
|
*
|
||||||
|
* Note that the hash table doesn't have a delete
|
||||||
|
* callback. If freeing of old data pointers is
|
||||||
|
* required to avoid memory leaks, perform a search
|
||||||
|
* before inserting.
|
||||||
|
*/
|
||||||
|
if (!entry_is_deleted(ht, entry) &&
|
||||||
|
entry->hash == hash &&
|
||||||
|
ht->key_equals_function(key, entry->key)) {
|
||||||
|
entry->key = key;
|
||||||
|
entry->data = data;
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_address += double_hash;
|
||||||
|
if (hash_address >= size)
|
||||||
|
hash_address -= size;
|
||||||
|
} while (hash_address != start_hash_address);
|
||||||
|
|
||||||
|
if (available_entry) {
|
||||||
|
if (entry_is_deleted(ht, available_entry))
|
||||||
|
ht->deleted_entries--;
|
||||||
|
available_entry->hash = hash;
|
||||||
|
available_entry->key = key;
|
||||||
|
available_entry->data = data;
|
||||||
|
ht->entries++;
|
||||||
|
return available_entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We could hit here if a required resize failed. An unchecked-malloc
|
||||||
|
* application could ignore this result.
|
||||||
|
*/
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts the key with the given hash into the table.
|
||||||
|
*
|
||||||
|
* Note that insertion may rearrange the table on a resize or rehash,
|
||||||
|
* so previously found hash_entries are no longer valid after this function.
|
||||||
|
*/
|
||||||
|
struct hash_entry *
|
||||||
|
_mesa_hash_table_insert(struct hash_table *ht, const void *key, void *data)
|
||||||
|
{
|
||||||
|
assert(ht->key_hash_function);
|
||||||
|
return hash_table_insert(ht, ht->key_hash_function(key), key, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct hash_entry *
|
||||||
|
_mesa_hash_table_insert_pre_hashed(struct hash_table *ht, uint32_t hash,
|
||||||
|
const void *key, void *data)
|
||||||
|
{
|
||||||
|
assert(ht->key_hash_function == NULL || hash == ht->key_hash_function(key));
|
||||||
|
return hash_table_insert(ht, hash, key, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function deletes the given hash table entry.
|
||||||
|
*
|
||||||
|
* Note that deletion doesn't otherwise modify the table, so an iteration over
|
||||||
|
* the table deleting entries is safe.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
_mesa_hash_table_remove(struct hash_table *ht,
|
||||||
|
struct hash_entry *entry)
|
||||||
|
{
|
||||||
|
if (!entry)
|
||||||
|
return;
|
||||||
|
|
||||||
|
entry->key = ht->deleted_key;
|
||||||
|
ht->entries--;
|
||||||
|
ht->deleted_entries++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the entry with the corresponding key, if exists.
|
||||||
|
*/
|
||||||
|
void _mesa_hash_table_remove_key(struct hash_table *ht,
|
||||||
|
const void *key)
|
||||||
|
{
|
||||||
|
_mesa_hash_table_remove(ht, _mesa_hash_table_search(ht, key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is an iterator over the hash table.
|
||||||
|
*
|
||||||
|
* Pass in NULL for the first entry, as in the start of a for loop. Note that
|
||||||
|
* an iteration over the table is O(table_size) not O(entries).
|
||||||
|
*/
|
||||||
|
struct hash_entry *
|
||||||
|
_mesa_hash_table_next_entry(struct hash_table *ht,
|
||||||
|
struct hash_entry *entry)
|
||||||
|
{
|
||||||
|
if (entry == NULL)
|
||||||
|
entry = ht->table;
|
||||||
|
else
|
||||||
|
entry = entry + 1;
|
||||||
|
|
||||||
|
for (; entry != ht->table + ht->size; entry++) {
|
||||||
|
if (entry_is_present(ht, entry)) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a random entry from the hash table.
|
||||||
|
*
|
||||||
|
* This may be useful in implementing random replacement (as opposed
|
||||||
|
* to just removing everything) in caches based on this hash table
|
||||||
|
* implementation. @predicate may be used to filter entries, or may
|
||||||
|
* be set to NULL for no filtering.
|
||||||
|
*/
|
||||||
|
struct hash_entry *
|
||||||
|
_mesa_hash_table_random_entry(struct hash_table *ht,
|
||||||
|
bool (*predicate)(struct hash_entry *entry))
|
||||||
|
{
|
||||||
|
struct hash_entry *entry;
|
||||||
|
uint32_t i = rand() % ht->size;
|
||||||
|
|
||||||
|
if (ht->entries == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
for (entry = ht->table + i; entry != ht->table + ht->size; entry++) {
|
||||||
|
if (entry_is_present(ht, entry) &&
|
||||||
|
(!predicate || predicate(entry))) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (entry = ht->table; entry != ht->table + i; entry++) {
|
||||||
|
if (entry_is_present(ht, entry) &&
|
||||||
|
(!predicate || predicate(entry))) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quick FNV-1a hash implementation based on:
|
||||||
|
* http://www.isthe.com/chongo/tech/comp/fnv/
|
||||||
|
*
|
||||||
|
* FNV-1a is not be the best hash out there -- Jenkins's lookup3 is supposed
|
||||||
|
* to be quite good, and it probably beats FNV. But FNV has the advantage
|
||||||
|
* that it involves almost no code. For an improvement on both, see Paul
|
||||||
|
* Hsieh's http://www.azillionmonkeys.com/qed/hash.html
|
||||||
|
*/
|
||||||
|
uint32_t
|
||||||
|
_mesa_hash_data(const void *data, size_t size)
|
||||||
|
{
|
||||||
|
return _mesa_fnv32_1a_accumulate_block(_mesa_fnv32_1a_offset_bias,
|
||||||
|
data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** FNV-1a string hash implementation */
|
||||||
|
uint32_t
|
||||||
|
_mesa_hash_string(const void *_key)
|
||||||
|
{
|
||||||
|
uint32_t hash = _mesa_fnv32_1a_offset_bias;
|
||||||
|
const char *key = _key;
|
||||||
|
|
||||||
|
while (*key != 0) {
|
||||||
|
hash = _mesa_fnv32_1a_accumulate(hash, *key);
|
||||||
|
key++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String compare function for use as the comparison callback in
|
||||||
|
* _mesa_hash_table_create().
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
_mesa_key_string_equal(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
return strcmp(a, b) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
_mesa_key_pointer_equal(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
return a == b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to create a hash table with pointer keys.
|
||||||
|
*/
|
||||||
|
struct hash_table *
|
||||||
|
_mesa_pointer_hash_table_create(void *mem_ctx)
|
||||||
|
{
|
||||||
|
return _mesa_hash_table_create(mem_ctx, _mesa_hash_pointer,
|
||||||
|
_mesa_key_pointer_equal);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash table wrapper which supports 64-bit keys.
|
||||||
|
*
|
||||||
|
* TODO: unify all hash table implementations.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct hash_key_u64 {
|
||||||
|
uint64_t value;
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint32_t
|
||||||
|
key_u64_hash(const void *key)
|
||||||
|
{
|
||||||
|
return _mesa_hash_data(key, sizeof(struct hash_key_u64));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
key_u64_equals(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const struct hash_key_u64 *aa = a;
|
||||||
|
const struct hash_key_u64 *bb = b;
|
||||||
|
|
||||||
|
return aa->value == bb->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define FREED_KEY_VALUE 0
|
||||||
|
|
||||||
|
struct hash_table_u64 *
|
||||||
|
_mesa_hash_table_u64_create(void *mem_ctx)
|
||||||
|
{
|
||||||
|
STATIC_ASSERT(FREED_KEY_VALUE != DELETED_KEY_VALUE);
|
||||||
|
struct hash_table_u64 *ht;
|
||||||
|
|
||||||
|
ht = CALLOC_STRUCT(hash_table_u64);
|
||||||
|
if (!ht)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (sizeof(void *) == 8) {
|
||||||
|
ht->table = _mesa_hash_table_create(mem_ctx, _mesa_hash_pointer,
|
||||||
|
_mesa_key_pointer_equal);
|
||||||
|
} else {
|
||||||
|
ht->table = _mesa_hash_table_create(mem_ctx, key_u64_hash,
|
||||||
|
key_u64_equals);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ht->table)
|
||||||
|
_mesa_hash_table_set_deleted_key(ht->table, uint_key(DELETED_KEY_VALUE));
|
||||||
|
|
||||||
|
return ht;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_mesa_hash_table_u64_clear(struct hash_table_u64 *ht,
|
||||||
|
void (*delete_function)(struct hash_entry *entry))
|
||||||
|
{
|
||||||
|
if (!ht)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ht->deleted_key_data) {
|
||||||
|
if (delete_function) {
|
||||||
|
struct hash_table *table = ht->table;
|
||||||
|
struct hash_entry entry;
|
||||||
|
|
||||||
|
/* Create a fake entry for the delete function. */
|
||||||
|
if (sizeof(void *) == 8) {
|
||||||
|
entry.hash = table->key_hash_function(table->deleted_key);
|
||||||
|
} else {
|
||||||
|
struct hash_key_u64 _key = { .value = (uintptr_t)table->deleted_key };
|
||||||
|
entry.hash = table->key_hash_function(&_key);
|
||||||
|
}
|
||||||
|
entry.key = table->deleted_key;
|
||||||
|
entry.data = ht->deleted_key_data;
|
||||||
|
|
||||||
|
delete_function(&entry);
|
||||||
|
}
|
||||||
|
ht->deleted_key_data = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ht->freed_key_data) {
|
||||||
|
if (delete_function) {
|
||||||
|
struct hash_table *table = ht->table;
|
||||||
|
struct hash_entry entry;
|
||||||
|
|
||||||
|
/* Create a fake entry for the delete function. */
|
||||||
|
if (sizeof(void *) == 8) {
|
||||||
|
entry.hash = table->key_hash_function(uint_key(FREED_KEY_VALUE));
|
||||||
|
} else {
|
||||||
|
struct hash_key_u64 _key = { .value = (uintptr_t)FREED_KEY_VALUE };
|
||||||
|
entry.hash = table->key_hash_function(&_key);
|
||||||
|
}
|
||||||
|
entry.key = uint_key(FREED_KEY_VALUE);
|
||||||
|
entry.data = ht->freed_key_data;
|
||||||
|
|
||||||
|
delete_function(&entry);
|
||||||
|
}
|
||||||
|
ht->freed_key_data = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
_mesa_hash_table_clear(ht->table, delete_function);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_mesa_hash_table_u64_destroy(struct hash_table_u64 *ht,
|
||||||
|
void (*delete_function)(struct hash_entry *entry))
|
||||||
|
{
|
||||||
|
if (!ht)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_mesa_hash_table_u64_clear(ht, delete_function);
|
||||||
|
_mesa_hash_table_destroy(ht->table, delete_function);
|
||||||
|
free(ht);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_mesa_hash_table_u64_insert(struct hash_table_u64 *ht, uint64_t key,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
if (key == FREED_KEY_VALUE) {
|
||||||
|
ht->freed_key_data = data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == DELETED_KEY_VALUE) {
|
||||||
|
ht->deleted_key_data = data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sizeof(void *) == 8) {
|
||||||
|
_mesa_hash_table_insert(ht->table, (void *)(uintptr_t)key, data);
|
||||||
|
} else {
|
||||||
|
struct hash_key_u64 *_key = CALLOC_STRUCT(hash_key_u64);
|
||||||
|
|
||||||
|
if (!_key)
|
||||||
|
return;
|
||||||
|
_key->value = key;
|
||||||
|
|
||||||
|
_mesa_hash_table_insert(ht->table, _key, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct hash_entry *
|
||||||
|
hash_table_u64_search(struct hash_table_u64 *ht, uint64_t key)
|
||||||
|
{
|
||||||
|
if (sizeof(void *) == 8) {
|
||||||
|
return _mesa_hash_table_search(ht->table, (void *)(uintptr_t)key);
|
||||||
|
} else {
|
||||||
|
struct hash_key_u64 _key = { .value = key };
|
||||||
|
return _mesa_hash_table_search(ht->table, &_key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
_mesa_hash_table_u64_search(struct hash_table_u64 *ht, uint64_t key)
|
||||||
|
{
|
||||||
|
struct hash_entry *entry;
|
||||||
|
|
||||||
|
if (key == FREED_KEY_VALUE)
|
||||||
|
return ht->freed_key_data;
|
||||||
|
|
||||||
|
if (key == DELETED_KEY_VALUE)
|
||||||
|
return ht->deleted_key_data;
|
||||||
|
|
||||||
|
entry = hash_table_u64_search(ht, key);
|
||||||
|
if (!entry)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return entry->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_mesa_hash_table_u64_remove(struct hash_table_u64 *ht, uint64_t key)
|
||||||
|
{
|
||||||
|
struct hash_entry *entry;
|
||||||
|
|
||||||
|
if (key == FREED_KEY_VALUE) {
|
||||||
|
ht->freed_key_data = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == DELETED_KEY_VALUE) {
|
||||||
|
ht->deleted_key_data = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = hash_table_u64_search(ht, key);
|
||||||
|
if (!entry)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (sizeof(void *) == 8) {
|
||||||
|
_mesa_hash_table_remove(ht->table, entry);
|
||||||
|
} else {
|
||||||
|
struct hash_key *_key = (struct hash_key *)entry->key;
|
||||||
|
|
||||||
|
_mesa_hash_table_remove(ht->table, entry);
|
||||||
|
free(_key);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,205 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2009,2012 Intel Corporation
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice (including the next
|
||||||
|
* paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
* Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
* IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Eric Anholt <eric@anholt.net>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _HASH_TABLE_H
|
||||||
|
#define _HASH_TABLE_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "../c99_compat.h"
|
||||||
|
#include "macros.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct hash_entry {
|
||||||
|
uint32_t hash;
|
||||||
|
const void *key;
|
||||||
|
void *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct hash_table {
|
||||||
|
struct hash_entry *table;
|
||||||
|
uint32_t (*key_hash_function)(const void *key);
|
||||||
|
bool (*key_equals_function)(const void *a, const void *b);
|
||||||
|
const void *deleted_key;
|
||||||
|
uint32_t size;
|
||||||
|
uint32_t rehash;
|
||||||
|
uint64_t size_magic;
|
||||||
|
uint64_t rehash_magic;
|
||||||
|
uint32_t max_entries;
|
||||||
|
uint32_t size_index;
|
||||||
|
uint32_t entries;
|
||||||
|
uint32_t deleted_entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct hash_table *
|
||||||
|
_mesa_hash_table_create(void *mem_ctx,
|
||||||
|
uint32_t (*key_hash_function)(const void *key),
|
||||||
|
bool (*key_equals_function)(const void *a,
|
||||||
|
const void *b));
|
||||||
|
|
||||||
|
bool
|
||||||
|
_mesa_hash_table_init(struct hash_table *ht,
|
||||||
|
void *mem_ctx,
|
||||||
|
uint32_t (*key_hash_function)(const void *key),
|
||||||
|
bool (*key_equals_function)(const void *a,
|
||||||
|
const void *b));
|
||||||
|
|
||||||
|
struct hash_table *
|
||||||
|
_mesa_hash_table_clone(struct hash_table *src, void *dst_mem_ctx);
|
||||||
|
void _mesa_hash_table_destroy(struct hash_table *ht,
|
||||||
|
void (*delete_function)(struct hash_entry *entry));
|
||||||
|
void _mesa_hash_table_clear(struct hash_table *ht,
|
||||||
|
void (*delete_function)(struct hash_entry *entry));
|
||||||
|
void _mesa_hash_table_set_deleted_key(struct hash_table *ht,
|
||||||
|
const void *deleted_key);
|
||||||
|
|
||||||
|
static inline uint32_t _mesa_hash_table_num_entries(struct hash_table *ht)
|
||||||
|
{
|
||||||
|
return ht->entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct hash_entry *
|
||||||
|
_mesa_hash_table_insert(struct hash_table *ht, const void *key, void *data);
|
||||||
|
struct hash_entry *
|
||||||
|
_mesa_hash_table_insert_pre_hashed(struct hash_table *ht, uint32_t hash,
|
||||||
|
const void *key, void *data);
|
||||||
|
struct hash_entry *
|
||||||
|
_mesa_hash_table_search(struct hash_table *ht, const void *key);
|
||||||
|
struct hash_entry *
|
||||||
|
_mesa_hash_table_search_pre_hashed(struct hash_table *ht, uint32_t hash,
|
||||||
|
const void *key);
|
||||||
|
void _mesa_hash_table_remove(struct hash_table *ht,
|
||||||
|
struct hash_entry *entry);
|
||||||
|
void _mesa_hash_table_remove_key(struct hash_table *ht,
|
||||||
|
const void *key);
|
||||||
|
|
||||||
|
struct hash_entry *_mesa_hash_table_next_entry(struct hash_table *ht,
|
||||||
|
struct hash_entry *entry);
|
||||||
|
struct hash_entry *
|
||||||
|
_mesa_hash_table_random_entry(struct hash_table *ht,
|
||||||
|
bool (*predicate)(struct hash_entry *entry));
|
||||||
|
|
||||||
|
uint32_t _mesa_hash_data(const void *data, size_t size);
|
||||||
|
uint32_t _mesa_hash_string(const void *key);
|
||||||
|
bool _mesa_key_string_equal(const void *a, const void *b);
|
||||||
|
bool _mesa_key_pointer_equal(const void *a, const void *b);
|
||||||
|
|
||||||
|
static inline uint32_t _mesa_key_hash_string(const void *key)
|
||||||
|
{
|
||||||
|
return _mesa_hash_string((const char *)key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t _mesa_hash_pointer(const void *pointer)
|
||||||
|
{
|
||||||
|
uintptr_t num = (uintptr_t) pointer;
|
||||||
|
return (uint32_t) ((num >> 2) ^ (num >> 6) ^ (num >> 10) ^ (num >> 14));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct hash_table *
|
||||||
|
_mesa_pointer_hash_table_create(void *mem_ctx);
|
||||||
|
|
||||||
|
enum {
|
||||||
|
_mesa_fnv32_1a_offset_bias = 2166136261u,
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline uint32_t
|
||||||
|
_mesa_fnv32_1a_accumulate_block(uint32_t hash, const void *data, size_t size)
|
||||||
|
{
|
||||||
|
const uint8_t *bytes = (const uint8_t *)data;
|
||||||
|
|
||||||
|
while (size-- != 0) {
|
||||||
|
hash ^= *bytes;
|
||||||
|
hash = hash * 0x01000193;
|
||||||
|
bytes++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define _mesa_fnv32_1a_accumulate(hash, expr) \
|
||||||
|
_mesa_fnv32_1a_accumulate_block(hash, &(expr), sizeof(expr))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This foreach function is safe against deletion (which just replaces
|
||||||
|
* an entry's data with the deleted marker), but not against insertion
|
||||||
|
* (which may rehash the table, making entry a dangling pointer).
|
||||||
|
*/
|
||||||
|
#define hash_table_foreach(ht, entry) \
|
||||||
|
for (struct hash_entry *entry = _mesa_hash_table_next_entry(ht, NULL); \
|
||||||
|
entry != NULL; \
|
||||||
|
entry = _mesa_hash_table_next_entry(ht, entry))
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
hash_table_call_foreach(struct hash_table *ht,
|
||||||
|
void (*callback)(const void *key,
|
||||||
|
void *data,
|
||||||
|
void *closure),
|
||||||
|
void *closure)
|
||||||
|
{
|
||||||
|
hash_table_foreach(ht, entry)
|
||||||
|
callback(entry->key, entry->data, closure);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash table wrapper which supports 64-bit keys.
|
||||||
|
*/
|
||||||
|
struct hash_table_u64 {
|
||||||
|
struct hash_table *table;
|
||||||
|
void *freed_key_data;
|
||||||
|
void *deleted_key_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct hash_table_u64 *
|
||||||
|
_mesa_hash_table_u64_create(void *mem_ctx);
|
||||||
|
|
||||||
|
void
|
||||||
|
_mesa_hash_table_u64_destroy(struct hash_table_u64 *ht,
|
||||||
|
void (*delete_function)(struct hash_entry *entry));
|
||||||
|
|
||||||
|
void
|
||||||
|
_mesa_hash_table_u64_insert(struct hash_table_u64 *ht, uint64_t key,
|
||||||
|
void *data);
|
||||||
|
|
||||||
|
void *
|
||||||
|
_mesa_hash_table_u64_search(struct hash_table_u64 *ht, uint64_t key);
|
||||||
|
|
||||||
|
void
|
||||||
|
_mesa_hash_table_u64_remove(struct hash_table_u64 *ht, uint64_t key);
|
||||||
|
|
||||||
|
void
|
||||||
|
_mesa_hash_table_u64_clear(struct hash_table_u64 *ht,
|
||||||
|
void (*delete_function)(struct hash_entry *entry));
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern C */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _HASH_TABLE_H */
|
@ -0,0 +1,249 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
*
|
||||||
|
* Copyright 2006 VMware, Inc., Bismarck, ND. USA.
|
||||||
|
* All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sub license, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||||
|
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice (including the
|
||||||
|
* next paragraph) shall be included in all copies or substantial portions
|
||||||
|
* of the Software.
|
||||||
|
*
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file
|
||||||
|
* List macros heavily inspired by the Linux kernel
|
||||||
|
* list handling. No list looping yet.
|
||||||
|
*
|
||||||
|
* Is not threadsafe, so common operations need to
|
||||||
|
* be protected using an external mutex.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _UTIL_LIST_H_
|
||||||
|
#define _UTIL_LIST_H_
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include "../c99_compat.h"
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
# define list_assert(cond, msg) assert(cond && msg)
|
||||||
|
#else
|
||||||
|
# define list_assert(cond, msg) (void)(0 && (cond))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct list_head
|
||||||
|
{
|
||||||
|
struct list_head *prev;
|
||||||
|
struct list_head *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void list_inithead(struct list_head *item)
|
||||||
|
{
|
||||||
|
item->prev = item;
|
||||||
|
item->next = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void list_add(struct list_head *item, struct list_head *list)
|
||||||
|
{
|
||||||
|
item->prev = list;
|
||||||
|
item->next = list->next;
|
||||||
|
list->next->prev = item;
|
||||||
|
list->next = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void list_addtail(struct list_head *item, struct list_head *list)
|
||||||
|
{
|
||||||
|
item->next = list;
|
||||||
|
item->prev = list->prev;
|
||||||
|
list->prev->next = item;
|
||||||
|
list->prev = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool list_is_empty(const struct list_head *list);
|
||||||
|
|
||||||
|
static inline void list_replace(struct list_head *from, struct list_head *to)
|
||||||
|
{
|
||||||
|
if (list_is_empty(from)) {
|
||||||
|
list_inithead(to);
|
||||||
|
} else {
|
||||||
|
to->prev = from->prev;
|
||||||
|
to->next = from->next;
|
||||||
|
from->next->prev = to;
|
||||||
|
from->prev->next = to;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void list_del(struct list_head *item)
|
||||||
|
{
|
||||||
|
item->prev->next = item->next;
|
||||||
|
item->next->prev = item->prev;
|
||||||
|
item->prev = item->next = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void list_delinit(struct list_head *item)
|
||||||
|
{
|
||||||
|
item->prev->next = item->next;
|
||||||
|
item->next->prev = item->prev;
|
||||||
|
item->next = item;
|
||||||
|
item->prev = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool list_is_empty(const struct list_head *list)
|
||||||
|
{
|
||||||
|
return list->next == list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the list has exactly one element.
|
||||||
|
*/
|
||||||
|
static inline bool list_is_singular(const struct list_head *list)
|
||||||
|
{
|
||||||
|
return list->next != NULL && list->next != list && list->next->next == list;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned list_length(const struct list_head *list)
|
||||||
|
{
|
||||||
|
struct list_head *node;
|
||||||
|
unsigned length = 0;
|
||||||
|
for (node = list->next; node != list; node = node->next)
|
||||||
|
length++;
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void list_splice(struct list_head *src, struct list_head *dst)
|
||||||
|
{
|
||||||
|
if (list_is_empty(src))
|
||||||
|
return;
|
||||||
|
|
||||||
|
src->next->prev = dst;
|
||||||
|
src->prev->next = dst->next;
|
||||||
|
dst->next->prev = src->prev;
|
||||||
|
dst->next = src->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void list_splicetail(struct list_head *src, struct list_head *dst)
|
||||||
|
{
|
||||||
|
if (list_is_empty(src))
|
||||||
|
return;
|
||||||
|
|
||||||
|
src->prev->next = dst;
|
||||||
|
src->next->prev = dst->prev;
|
||||||
|
dst->prev->next = src->next;
|
||||||
|
dst->prev = src->prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void list_validate(const struct list_head *list)
|
||||||
|
{
|
||||||
|
struct list_head *node;
|
||||||
|
assert(list->next->prev == list && list->prev->next == list);
|
||||||
|
for (node = list->next; node != list; node = node->next)
|
||||||
|
assert(node->next->prev == node && node->prev->next == node);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LIST_ENTRY(__type, __item, __field) \
|
||||||
|
((__type *)(((char *)(__item)) - offsetof(__type, __field)))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cast from a pointer to a member of a struct back to the containing struct.
|
||||||
|
*
|
||||||
|
* 'sample' MUST be initialized, or else the result is undefined!
|
||||||
|
*/
|
||||||
|
#ifndef container_of
|
||||||
|
#define container_of(ptr, sample, member) \
|
||||||
|
(void *)((char *)(ptr) \
|
||||||
|
- ((char *)&(sample)->member - (char *)(sample)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define list_first_entry(ptr, type, member) \
|
||||||
|
LIST_ENTRY(type, (ptr)->next, member)
|
||||||
|
|
||||||
|
#define list_last_entry(ptr, type, member) \
|
||||||
|
LIST_ENTRY(type, (ptr)->prev, member)
|
||||||
|
|
||||||
|
|
||||||
|
#define LIST_FOR_EACH_ENTRY(pos, head, member) \
|
||||||
|
for (pos = NULL, pos = container_of((head)->next, pos, member); \
|
||||||
|
&pos->member != (head); \
|
||||||
|
pos = container_of(pos->member.next, pos, member))
|
||||||
|
|
||||||
|
#define LIST_FOR_EACH_ENTRY_SAFE(pos, storage, head, member) \
|
||||||
|
for (pos = NULL, pos = container_of((head)->next, pos, member), \
|
||||||
|
storage = container_of(pos->member.next, pos, member); \
|
||||||
|
&pos->member != (head); \
|
||||||
|
pos = storage, storage = container_of(storage->member.next, storage, member))
|
||||||
|
|
||||||
|
#define LIST_FOR_EACH_ENTRY_SAFE_REV(pos, storage, head, member) \
|
||||||
|
for (pos = NULL, pos = container_of((head)->prev, pos, member), \
|
||||||
|
storage = container_of(pos->member.prev, pos, member); \
|
||||||
|
&pos->member != (head); \
|
||||||
|
pos = storage, storage = container_of(storage->member.prev, storage, member))
|
||||||
|
|
||||||
|
#define LIST_FOR_EACH_ENTRY_FROM(pos, start, head, member) \
|
||||||
|
for (pos = NULL, pos = container_of((start), pos, member); \
|
||||||
|
&pos->member != (head); \
|
||||||
|
pos = container_of(pos->member.next, pos, member))
|
||||||
|
|
||||||
|
#define LIST_FOR_EACH_ENTRY_FROM_REV(pos, start, head, member) \
|
||||||
|
for (pos = NULL, pos = container_of((start), pos, member); \
|
||||||
|
&pos->member != (head); \
|
||||||
|
pos = container_of(pos->member.prev, pos, member))
|
||||||
|
|
||||||
|
#define list_for_each_entry(type, pos, head, member) \
|
||||||
|
for (type *pos = LIST_ENTRY(type, (head)->next, member), \
|
||||||
|
*__next = LIST_ENTRY(type, pos->member.next, member); \
|
||||||
|
&pos->member != (head); \
|
||||||
|
pos = LIST_ENTRY(type, pos->member.next, member), \
|
||||||
|
list_assert(pos == __next, "use _safe iterator"), \
|
||||||
|
__next = LIST_ENTRY(type, __next->member.next, member))
|
||||||
|
|
||||||
|
#define list_for_each_entry_safe(type, pos, head, member) \
|
||||||
|
for (type *pos = LIST_ENTRY(type, (head)->next, member), \
|
||||||
|
*__next = LIST_ENTRY(type, pos->member.next, member); \
|
||||||
|
&pos->member != (head); \
|
||||||
|
pos = __next, \
|
||||||
|
__next = LIST_ENTRY(type, __next->member.next, member))
|
||||||
|
|
||||||
|
#define list_for_each_entry_rev(type, pos, head, member) \
|
||||||
|
for (type *pos = LIST_ENTRY(type, (head)->prev, member), \
|
||||||
|
*__prev = LIST_ENTRY(type, pos->member.prev, member); \
|
||||||
|
&pos->member != (head); \
|
||||||
|
pos = LIST_ENTRY(type, pos->member.prev, member), \
|
||||||
|
list_assert(pos == __prev, "use _safe iterator"), \
|
||||||
|
__prev = LIST_ENTRY(type, __prev->member.prev, member))
|
||||||
|
|
||||||
|
#define list_for_each_entry_safe_rev(type, pos, head, member) \
|
||||||
|
for (type *pos = LIST_ENTRY(type, (head)->prev, member), \
|
||||||
|
*__prev = LIST_ENTRY(type, pos->member.prev, member); \
|
||||||
|
&pos->member != (head); \
|
||||||
|
pos = __prev, \
|
||||||
|
__prev = LIST_ENTRY(type, __prev->member.prev, member))
|
||||||
|
|
||||||
|
#define list_for_each_entry_from(type, pos, start, head, member) \
|
||||||
|
for (type *pos = LIST_ENTRY(type, (start), member); \
|
||||||
|
&pos->member != (head); \
|
||||||
|
pos = LIST_ENTRY(type, pos->member.next, member))
|
||||||
|
|
||||||
|
#define list_for_each_entry_from_rev(type, pos, start, head, member) \
|
||||||
|
for (type *pos = LIST_ENTRY(type, (start), member); \
|
||||||
|
&pos->member != (head); \
|
||||||
|
pos = LIST_ENTRY(type, pos->member.prev, member))
|
||||||
|
|
||||||
|
#endif /*_UTIL_LIST_H_*/
|
@ -0,0 +1,335 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2014 Intel Corporation
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice (including the next
|
||||||
|
* paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
* Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
* IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef UTIL_MACROS_H
|
||||||
|
#define UTIL_MACROS_H
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "../c99_compat.h"
|
||||||
|
#include "../c11_compat.h"
|
||||||
|
|
||||||
|
/* Compute the size of an array */
|
||||||
|
#ifndef ARRAY_SIZE
|
||||||
|
# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* For compatibility with Clang's __has_builtin() */
|
||||||
|
#ifndef __has_builtin
|
||||||
|
# define __has_builtin(x) 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __builtin_expect macros
|
||||||
|
*/
|
||||||
|
#if !defined(HAVE___BUILTIN_EXPECT)
|
||||||
|
# define __builtin_expect(x, y) (x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef likely
|
||||||
|
# ifdef HAVE___BUILTIN_EXPECT
|
||||||
|
# define likely(x) __builtin_expect(!!(x), 1)
|
||||||
|
# define unlikely(x) __builtin_expect(!!(x), 0)
|
||||||
|
# else
|
||||||
|
# define likely(x) (x)
|
||||||
|
# define unlikely(x) (x)
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static (compile-time) assertion.
|
||||||
|
* Basically, use COND to dimension an array. If COND is false/zero the
|
||||||
|
* array size will be -1 and we'll get a compilation error.
|
||||||
|
*/
|
||||||
|
#define STATIC_ASSERT(COND) \
|
||||||
|
do { \
|
||||||
|
(void) sizeof(char [1 - 2*!(COND)]); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unreachable macro. Useful for suppressing "control reaches end of non-void
|
||||||
|
* function" warnings.
|
||||||
|
*/
|
||||||
|
#if defined(HAVE___BUILTIN_UNREACHABLE) || __has_builtin(__builtin_unreachable)
|
||||||
|
#define unreachable(str) \
|
||||||
|
do { \
|
||||||
|
assert(!str); \
|
||||||
|
__builtin_unreachable(); \
|
||||||
|
} while (0)
|
||||||
|
#elif defined (_MSC_VER)
|
||||||
|
#define unreachable(str) \
|
||||||
|
do { \
|
||||||
|
assert(!str); \
|
||||||
|
__assume(0); \
|
||||||
|
} while (0)
|
||||||
|
#else
|
||||||
|
#define unreachable(str) assert(!str)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assume macro. Useful for expressing our assumptions to the compiler,
|
||||||
|
* typically for purposes of silencing warnings.
|
||||||
|
*/
|
||||||
|
#if __has_builtin(__builtin_assume)
|
||||||
|
#define assume(expr) \
|
||||||
|
do { \
|
||||||
|
assert(expr); \
|
||||||
|
__builtin_assume(expr); \
|
||||||
|
} while (0)
|
||||||
|
#elif defined HAVE___BUILTIN_UNREACHABLE
|
||||||
|
#define assume(expr) ((expr) ? ((void) 0) \
|
||||||
|
: (assert(!"assumption failed"), \
|
||||||
|
__builtin_unreachable()))
|
||||||
|
#elif defined (_MSC_VER)
|
||||||
|
#define assume(expr) __assume(expr)
|
||||||
|
#else
|
||||||
|
#define assume(expr) assert(expr)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Attribute const is used for functions that have no effects other than their
|
||||||
|
* return value, and only rely on the argument values to compute the return
|
||||||
|
* value. As a result, calls to it can be CSEed. Note that using memory
|
||||||
|
* pointed to by the arguments is not allowed for const functions.
|
||||||
|
*/
|
||||||
|
#ifdef HAVE_FUNC_ATTRIBUTE_CONST
|
||||||
|
#define ATTRIBUTE_CONST __attribute__((__const__))
|
||||||
|
#else
|
||||||
|
#define ATTRIBUTE_CONST
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_FUNC_ATTRIBUTE_FLATTEN
|
||||||
|
#define FLATTEN __attribute__((__flatten__))
|
||||||
|
#else
|
||||||
|
#define FLATTEN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_FUNC_ATTRIBUTE_FORMAT
|
||||||
|
#define PRINTFLIKE(f, a) __attribute__ ((format(__printf__, f, a)))
|
||||||
|
#else
|
||||||
|
#define PRINTFLIKE(f, a)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_FUNC_ATTRIBUTE_MALLOC
|
||||||
|
#define MALLOCLIKE __attribute__((__malloc__))
|
||||||
|
#else
|
||||||
|
#define MALLOCLIKE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Forced function inlining */
|
||||||
|
/* Note: Clang also sets __GNUC__ (see other cases below) */
|
||||||
|
#ifndef ALWAYS_INLINE
|
||||||
|
# if defined(__GNUC__)
|
||||||
|
# define ALWAYS_INLINE inline __attribute__((always_inline))
|
||||||
|
# elif defined(_MSC_VER)
|
||||||
|
# define ALWAYS_INLINE __forceinline
|
||||||
|
# else
|
||||||
|
# define ALWAYS_INLINE inline
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Used to optionally mark structures with misaligned elements or size as
|
||||||
|
* packed, to trade off performance for space.
|
||||||
|
*/
|
||||||
|
#ifdef HAVE_FUNC_ATTRIBUTE_PACKED
|
||||||
|
#define PACKED __attribute__((__packed__))
|
||||||
|
#else
|
||||||
|
#define PACKED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Attribute pure is used for functions that have no effects other than their
|
||||||
|
* return value. As a result, calls to it can be dead code eliminated.
|
||||||
|
*/
|
||||||
|
#ifdef HAVE_FUNC_ATTRIBUTE_PURE
|
||||||
|
#define ATTRIBUTE_PURE __attribute__((__pure__))
|
||||||
|
#else
|
||||||
|
#define ATTRIBUTE_PURE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_FUNC_ATTRIBUTE_RETURNS_NONNULL
|
||||||
|
#define ATTRIBUTE_RETURNS_NONNULL __attribute__((__returns_nonnull__))
|
||||||
|
#else
|
||||||
|
#define ATTRIBUTE_RETURNS_NONNULL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NORETURN
|
||||||
|
# ifdef _MSC_VER
|
||||||
|
# define NORETURN __declspec(noreturn)
|
||||||
|
# elif defined HAVE_FUNC_ATTRIBUTE_NORETURN
|
||||||
|
# define NORETURN __attribute__((__noreturn__))
|
||||||
|
# else
|
||||||
|
# define NORETURN
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
/**
|
||||||
|
* Macro function that evaluates to true if T is a trivially
|
||||||
|
* destructible type -- that is, if its (non-virtual) destructor
|
||||||
|
* performs no action and all member variables and base classes are
|
||||||
|
* trivially destructible themselves.
|
||||||
|
*/
|
||||||
|
# if (defined(__clang__) && defined(__has_feature))
|
||||||
|
# if __has_feature(has_trivial_destructor)
|
||||||
|
# define HAS_TRIVIAL_DESTRUCTOR(T) __has_trivial_destructor(T)
|
||||||
|
# endif
|
||||||
|
# elif defined(__GNUC__)
|
||||||
|
# if ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3)))
|
||||||
|
# define HAS_TRIVIAL_DESTRUCTOR(T) __has_trivial_destructor(T)
|
||||||
|
# endif
|
||||||
|
# elif defined(_MSC_VER) && !defined(__INTEL_COMPILER)
|
||||||
|
# define HAS_TRIVIAL_DESTRUCTOR(T) __has_trivial_destructor(T)
|
||||||
|
# endif
|
||||||
|
# ifndef HAS_TRIVIAL_DESTRUCTOR
|
||||||
|
/* It's always safe (if inefficient) to assume that a
|
||||||
|
* destructor is non-trivial.
|
||||||
|
*/
|
||||||
|
# define HAS_TRIVIAL_DESTRUCTOR(T) (false)
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PUBLIC/USED macros
|
||||||
|
*
|
||||||
|
* If we build the library with gcc's -fvisibility=hidden flag, we'll
|
||||||
|
* use the PUBLIC macro to mark functions that are to be exported.
|
||||||
|
*
|
||||||
|
* We also need to define a USED attribute, so the optimizer doesn't
|
||||||
|
* inline a static function that we later use in an alias. - ajax
|
||||||
|
*/
|
||||||
|
#ifndef PUBLIC
|
||||||
|
# if defined(__GNUC__)
|
||||||
|
# define PUBLIC __attribute__((visibility("default")))
|
||||||
|
# define USED __attribute__((used))
|
||||||
|
# elif defined(_MSC_VER)
|
||||||
|
# define PUBLIC __declspec(dllexport)
|
||||||
|
# define USED
|
||||||
|
# else
|
||||||
|
# define PUBLIC
|
||||||
|
# define USED
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UNUSED marks variables (or sometimes functions) that have to be defined,
|
||||||
|
* but are sometimes (or always) unused beyond that. A common case is for
|
||||||
|
* a function parameter to be used in some build configurations but not others.
|
||||||
|
* Another case is fallback vfuncs that don't do anything with their params.
|
||||||
|
*
|
||||||
|
* Note that this should not be used for identifiers used in `assert()`;
|
||||||
|
* see ASSERTED below.
|
||||||
|
*/
|
||||||
|
#ifdef HAVE_FUNC_ATTRIBUTE_UNUSED
|
||||||
|
#define UNUSED __attribute__((unused))
|
||||||
|
#else
|
||||||
|
#define UNUSED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use ASSERTED to indicate that an identifier is unused outside of an `assert()`,
|
||||||
|
* so that assert-free builds don't get "unused variable" warnings.
|
||||||
|
*/
|
||||||
|
#ifdef NDEBUG
|
||||||
|
#define ASSERTED UNUSED
|
||||||
|
#else
|
||||||
|
#define ASSERTED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_FUNC_ATTRIBUTE_WARN_UNUSED_RESULT
|
||||||
|
#define MUST_CHECK __attribute__((warn_unused_result))
|
||||||
|
#else
|
||||||
|
#define MUST_CHECK
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#define ATTRIBUTE_NOINLINE __attribute__((noinline))
|
||||||
|
#else
|
||||||
|
#define ATTRIBUTE_NOINLINE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that STRUCT::FIELD can hold MAXVAL. We use a lot of bitfields
|
||||||
|
* in Mesa/gallium. We have to be sure they're of sufficient size to
|
||||||
|
* hold the largest expected value.
|
||||||
|
* Note that with MSVC, enums are signed and enum bitfields need one extra
|
||||||
|
* high bit (always zero) to ensure the max value is handled correctly.
|
||||||
|
* This macro will detect that with MSVC, but not GCC.
|
||||||
|
*/
|
||||||
|
#define ASSERT_BITFIELD_SIZE(STRUCT, FIELD, MAXVAL) \
|
||||||
|
do { \
|
||||||
|
ASSERTED STRUCT s; \
|
||||||
|
s.FIELD = (MAXVAL); \
|
||||||
|
assert((int) s.FIELD == (MAXVAL) && "Insufficient bitfield size!"); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
|
/** Compute ceiling of integer quotient of A divided by B. */
|
||||||
|
#define DIV_ROUND_UP( A, B ) ( ((A) + (B) - 1) / (B) )
|
||||||
|
|
||||||
|
/** Clamp X to [MIN,MAX]. Turn NaN into MIN, arbitrarily. */
|
||||||
|
#define CLAMP( X, MIN, MAX ) ( (X)>(MIN) ? ((X)>(MAX) ? (MAX) : (X)) : (MIN) )
|
||||||
|
|
||||||
|
/** Minimum of two values: */
|
||||||
|
#define MIN2( A, B ) ( (A)<(B) ? (A) : (B) )
|
||||||
|
|
||||||
|
/** Maximum of two values: */
|
||||||
|
#define MAX2( A, B ) ( (A)>(B) ? (A) : (B) )
|
||||||
|
|
||||||
|
/** Minimum and maximum of three values: */
|
||||||
|
#define MIN3( A, B, C ) ((A) < (B) ? MIN2(A, C) : MIN2(B, C))
|
||||||
|
#define MAX3( A, B, C ) ((A) > (B) ? MAX2(A, C) : MAX2(B, C))
|
||||||
|
|
||||||
|
/** Align a value to a power of two */
|
||||||
|
#define ALIGN_POT(x, pot_align) (((x) + (pot_align) - 1) & ~((pot_align) - 1))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Macro for declaring an explicit conversion operator. Defaults to an
|
||||||
|
* implicit conversion if C++11 is not supported.
|
||||||
|
*/
|
||||||
|
#if __cplusplus >= 201103L
|
||||||
|
#define EXPLICIT_CONVERSION explicit
|
||||||
|
#elif defined(__cplusplus)
|
||||||
|
#define EXPLICIT_CONVERSION
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Set a single bit */
|
||||||
|
#define BITFIELD_BIT(b) (1u << (b))
|
||||||
|
/** Set all bits up to excluding bit b */
|
||||||
|
#define BITFIELD_MASK(b) \
|
||||||
|
((b) == 32 ? (~0u) : BITFIELD_BIT((b) % 32) - 1)
|
||||||
|
/** Set count bits starting from bit b */
|
||||||
|
#define BITFIELD_RANGE(b, count) \
|
||||||
|
(BITFIELD_MASK((b) + (count)) & ~BITFIELD_MASK(b))
|
||||||
|
|
||||||
|
/** Set a single bit */
|
||||||
|
#define BITFIELD64_BIT(b) (1ull << (b))
|
||||||
|
/** Set all bits up to excluding bit b */
|
||||||
|
#define BITFIELD64_MASK(b) \
|
||||||
|
((b) == 64 ? (~0ull) : BITFIELD64_BIT(b) - 1)
|
||||||
|
/** Set count bits starting from bit b */
|
||||||
|
#define BITFIELD64_RANGE(b, count) \
|
||||||
|
(BITFIELD64_MASK((b) + (count)) & ~BITFIELD64_MASK(b))
|
||||||
|
|
||||||
|
#endif /* UTIL_MACROS_H */
|
@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 Intel Corporation
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "os_socket.h"
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
os_socket_listen_abstract(const char *path, int count)
|
||||||
|
{
|
||||||
|
int s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (s < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
struct sockaddr_un addr;
|
||||||
|
memset(&addr, 0, sizeof(addr));
|
||||||
|
addr.sun_family = AF_UNIX;
|
||||||
|
strncpy(addr.sun_path + 1, path, sizeof(addr.sun_path) - 2);
|
||||||
|
|
||||||
|
/* Create an abstract socket */
|
||||||
|
int ret = bind(s, (struct sockaddr*)&addr,
|
||||||
|
offsetof(struct sockaddr_un, sun_path) +
|
||||||
|
strlen(path) + 1);
|
||||||
|
if (ret < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
listen(s, count);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
os_socket_accept(int s)
|
||||||
|
{
|
||||||
|
return accept(s, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t
|
||||||
|
os_socket_recv(int socket, void *buffer, size_t length, int flags)
|
||||||
|
{
|
||||||
|
return recv(socket, buffer, length, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t
|
||||||
|
os_socket_send(int socket, const void *buffer, size_t length, int flags)
|
||||||
|
{
|
||||||
|
return send(socket, buffer, length, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
os_socket_block(int s, bool block)
|
||||||
|
{
|
||||||
|
int old = fcntl(s, F_GETFL, 0);
|
||||||
|
if (old == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* TODO obey block */
|
||||||
|
if (block)
|
||||||
|
fcntl(s, F_SETFL, old & ~O_NONBLOCK);
|
||||||
|
else
|
||||||
|
fcntl(s, F_SETFL, old | O_NONBLOCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
os_socket_close(int s)
|
||||||
|
{
|
||||||
|
close(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
int
|
||||||
|
os_socket_listen_abstract(const char *path, int count)
|
||||||
|
{
|
||||||
|
errno = -ENOSYS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
os_socket_accept(int s)
|
||||||
|
{
|
||||||
|
errno = -ENOSYS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t
|
||||||
|
os_socket_recv(int socket, void *buffer, size_t length, int flags)
|
||||||
|
{
|
||||||
|
errno = -ENOSYS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t
|
||||||
|
os_socket_send(int socket, const void *buffer, size_t length, int flags)
|
||||||
|
{
|
||||||
|
errno = -ENOSYS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
os_socket_block(int s, bool block)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
os_socket_close(int s)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 Intel Corporation
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Socket operations helpers
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _OS_SOCKET_H_
|
||||||
|
#define _OS_SOCKET_H_
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <BaseTsd.h>
|
||||||
|
typedef SSIZE_T ssize_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int os_socket_accept(int s);
|
||||||
|
|
||||||
|
int os_socket_listen_abstract(const char *path, int count);
|
||||||
|
|
||||||
|
ssize_t os_socket_recv(int socket, void *buffer, size_t length, int flags);
|
||||||
|
ssize_t os_socket_send(int socket, const void *buffer, size_t length, int flags);
|
||||||
|
|
||||||
|
void os_socket_block(int s, bool block);
|
||||||
|
void os_socket_close(int s);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _OS_SOCKET_H_ */
|
@ -0,0 +1,195 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
*
|
||||||
|
* Copyright 2008-2010 VMware, Inc.
|
||||||
|
* All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sub license, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice (including the
|
||||||
|
* next paragraph) shall be included in all copies or substantial portions
|
||||||
|
* of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
|
||||||
|
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* OS independent time-manipulation functions.
|
||||||
|
*
|
||||||
|
* @author Jose Fonseca <jfonseca@vmware.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "os_time.h"
|
||||||
|
#include "detect_os.h"
|
||||||
|
|
||||||
|
#if defined(USE_GCC_ATOMIC_BUILTINS)
|
||||||
|
/* The builtins with explicit memory model are available since GCC 4.7. */
|
||||||
|
#define p_atomic_read(_v) __atomic_load_n((_v), __ATOMIC_ACQUIRE)
|
||||||
|
#else
|
||||||
|
#define p_atomic_read(_v) (*(_v))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if DETECT_OS_UNIX
|
||||||
|
# include <unistd.h> /* usleep */
|
||||||
|
# include <time.h> /* timeval */
|
||||||
|
# include <sys/time.h> /* timeval */
|
||||||
|
# include <sched.h> /* sched_yield */
|
||||||
|
# include <errno.h>
|
||||||
|
#elif DETECT_OS_WINDOWS
|
||||||
|
# include <windows.h>
|
||||||
|
#else
|
||||||
|
# error Unsupported OS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
int64_t
|
||||||
|
os_time_get_nano(void)
|
||||||
|
{
|
||||||
|
#if DETECT_OS_LINUX || DETECT_OS_BSD
|
||||||
|
|
||||||
|
struct timespec tv;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &tv);
|
||||||
|
return tv.tv_nsec + tv.tv_sec*INT64_C(1000000000);
|
||||||
|
|
||||||
|
#elif DETECT_OS_UNIX
|
||||||
|
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
return tv.tv_usec*INT64_C(1000) + tv.tv_sec*INT64_C(1000000000);
|
||||||
|
|
||||||
|
#elif DETECT_OS_WINDOWS
|
||||||
|
|
||||||
|
static LARGE_INTEGER frequency;
|
||||||
|
LARGE_INTEGER counter;
|
||||||
|
int64_t secs, nanosecs;
|
||||||
|
if(!frequency.QuadPart)
|
||||||
|
QueryPerformanceFrequency(&frequency);
|
||||||
|
QueryPerformanceCounter(&counter);
|
||||||
|
/* Compute seconds and nanoseconds parts separately to
|
||||||
|
* reduce severity of precision loss.
|
||||||
|
*/
|
||||||
|
secs = counter.QuadPart / frequency.QuadPart;
|
||||||
|
nanosecs = (counter.QuadPart % frequency.QuadPart) * INT64_C(1000000000)
|
||||||
|
/ frequency.QuadPart;
|
||||||
|
return secs*INT64_C(1000000000) + nanosecs;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#error Unsupported OS
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
os_time_sleep(int64_t usecs)
|
||||||
|
{
|
||||||
|
#if DETECT_OS_LINUX
|
||||||
|
struct timespec time;
|
||||||
|
time.tv_sec = usecs / 1000000;
|
||||||
|
time.tv_nsec = (usecs % 1000000) * 1000;
|
||||||
|
while (clock_nanosleep(CLOCK_MONOTONIC, 0, &time, &time) == EINTR);
|
||||||
|
|
||||||
|
#elif DETECT_OS_UNIX
|
||||||
|
usleep(usecs);
|
||||||
|
|
||||||
|
#elif DETECT_OS_WINDOWS
|
||||||
|
DWORD dwMilliseconds = (DWORD) ((usecs + 999) / 1000);
|
||||||
|
/* Avoid Sleep(O) as that would cause to sleep for an undetermined duration */
|
||||||
|
if (dwMilliseconds) {
|
||||||
|
Sleep(dwMilliseconds);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
# error Unsupported OS
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int64_t
|
||||||
|
os_time_get_absolute_timeout(uint64_t timeout)
|
||||||
|
{
|
||||||
|
int64_t time, abs_timeout;
|
||||||
|
|
||||||
|
/* Also check for the type upper bound. */
|
||||||
|
if (timeout == OS_TIMEOUT_INFINITE || timeout > INT64_MAX)
|
||||||
|
return OS_TIMEOUT_INFINITE;
|
||||||
|
|
||||||
|
time = os_time_get_nano();
|
||||||
|
abs_timeout = time + (int64_t)timeout;
|
||||||
|
|
||||||
|
/* Check for overflow. */
|
||||||
|
if (abs_timeout < time)
|
||||||
|
return OS_TIMEOUT_INFINITE;
|
||||||
|
|
||||||
|
return abs_timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
os_wait_until_zero(volatile int *var, uint64_t timeout)
|
||||||
|
{
|
||||||
|
if (!p_atomic_read(var))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!timeout)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (timeout == OS_TIMEOUT_INFINITE) {
|
||||||
|
while (p_atomic_read(var)) {
|
||||||
|
#if DETECT_OS_UNIX
|
||||||
|
sched_yield();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int64_t start_time = os_time_get_nano();
|
||||||
|
int64_t end_time = start_time + timeout;
|
||||||
|
|
||||||
|
while (p_atomic_read(var)) {
|
||||||
|
if (os_time_timeout(start_time, end_time, os_time_get_nano()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#if DETECT_OS_UNIX
|
||||||
|
sched_yield();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
os_wait_until_zero_abs_timeout(volatile int *var, int64_t timeout)
|
||||||
|
{
|
||||||
|
if (!p_atomic_read(var))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (timeout == OS_TIMEOUT_INFINITE)
|
||||||
|
return os_wait_until_zero(var, OS_TIMEOUT_INFINITE);
|
||||||
|
|
||||||
|
while (p_atomic_read(var)) {
|
||||||
|
if (os_time_get_nano() >= timeout)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#if DETECT_OS_UNIX
|
||||||
|
sched_yield();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
@ -0,0 +1,130 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
*
|
||||||
|
* Copyright 2008-2010 VMware, Inc.
|
||||||
|
* All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sub license, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice (including the
|
||||||
|
* next paragraph) shall be included in all copies or substantial portions
|
||||||
|
* of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
|
||||||
|
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* OS independent time-manipulation functions.
|
||||||
|
*
|
||||||
|
* @author Jose Fonseca <jfonseca@vmware.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _OS_TIME_H_
|
||||||
|
#define _OS_TIME_H_
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* must be equal to PIPE_TIMEOUT_INFINITE */
|
||||||
|
#define OS_TIMEOUT_INFINITE 0xffffffffffffffffull
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the current time in nanoseconds from an unknown base.
|
||||||
|
*/
|
||||||
|
int64_t
|
||||||
|
os_time_get_nano(void);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the current time in microseconds from an unknown base.
|
||||||
|
*/
|
||||||
|
static inline int64_t
|
||||||
|
os_time_get(void)
|
||||||
|
{
|
||||||
|
return os_time_get_nano() / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sleep.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
os_time_sleep(int64_t usecs);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper function for detecting time outs, taking in account overflow.
|
||||||
|
*
|
||||||
|
* Returns true if the current time has elapsed beyond the specified interval.
|
||||||
|
*/
|
||||||
|
static inline bool
|
||||||
|
os_time_timeout(int64_t start,
|
||||||
|
int64_t end,
|
||||||
|
int64_t curr)
|
||||||
|
{
|
||||||
|
if (start <= end)
|
||||||
|
return !(start <= curr && curr < end);
|
||||||
|
else
|
||||||
|
return !((start <= curr) || (curr < end));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a relative timeout in nanoseconds into an absolute timeout,
|
||||||
|
* in other words, it returns current time + timeout.
|
||||||
|
* os_time_get_nano() must be monotonic.
|
||||||
|
* OS_TIMEOUT_INFINITE is passed through unchanged. If the calculation
|
||||||
|
* overflows, OS_TIMEOUT_INFINITE is returned.
|
||||||
|
*/
|
||||||
|
int64_t
|
||||||
|
os_time_get_absolute_timeout(uint64_t timeout);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait until the variable at the given memory location is zero.
|
||||||
|
*
|
||||||
|
* \param var variable
|
||||||
|
* \param timeout timeout in ns, can be anything from 0 (no wait) to
|
||||||
|
* OS_TIMEOUT_INFINITE (wait forever)
|
||||||
|
* \return true if the variable is zero
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
os_wait_until_zero(volatile int *var, uint64_t timeout);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait until the variable at the given memory location is zero.
|
||||||
|
* The timeout is the absolute time when the waiting should stop. If it is
|
||||||
|
* less than or equal to the current time, it only returns the status and
|
||||||
|
* doesn't wait. OS_TIMEOUT_INFINITE waits forever. This requires that
|
||||||
|
* os_time_get_nano is monotonic.
|
||||||
|
*
|
||||||
|
* \param var variable
|
||||||
|
* \param timeout the time in ns when the waiting should stop
|
||||||
|
* \return true if the variable is zero
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
os_wait_until_zero_abs_timeout(volatile int *var, int64_t timeout);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _OS_TIME_H_ */
|
@ -0,0 +1,920 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2010 Intel Corporation
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice (including the next
|
||||||
|
* paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
* Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* Some versions of MinGW are missing _vscprintf's declaration, although they
|
||||||
|
* still provide the symbol in the import library. */
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
_CRTIMP int _vscprintf(const char *format, va_list argptr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "ralloc.h"
|
||||||
|
|
||||||
|
#ifndef va_copy
|
||||||
|
#ifdef __va_copy
|
||||||
|
#define va_copy(dest, src) __va_copy((dest), (src))
|
||||||
|
#else
|
||||||
|
#define va_copy(dest, src) (dest) = (src)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CANARY 0x5A1106
|
||||||
|
|
||||||
|
/* Align the header's size so that ralloc() allocations will return with the
|
||||||
|
* same alignment as a libc malloc would have (8 on 32-bit GLIBC, 16 on
|
||||||
|
* 64-bit), avoiding performance penalities on x86 and alignment faults on
|
||||||
|
* ARM.
|
||||||
|
*/
|
||||||
|
struct
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
__declspec(align(8))
|
||||||
|
#elif defined(__LP64__)
|
||||||
|
__attribute__((aligned(16)))
|
||||||
|
#else
|
||||||
|
__attribute__((aligned(8)))
|
||||||
|
#endif
|
||||||
|
ralloc_header
|
||||||
|
{
|
||||||
|
#ifndef NDEBUG
|
||||||
|
/* A canary value used to determine whether a pointer is ralloc'd. */
|
||||||
|
unsigned canary;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct ralloc_header *parent;
|
||||||
|
|
||||||
|
/* The first child (head of a linked list) */
|
||||||
|
struct ralloc_header *child;
|
||||||
|
|
||||||
|
/* Linked list of siblings */
|
||||||
|
struct ralloc_header *prev;
|
||||||
|
struct ralloc_header *next;
|
||||||
|
|
||||||
|
void (*destructor)(void *);
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct ralloc_header ralloc_header;
|
||||||
|
|
||||||
|
static void unlink_block(ralloc_header *info);
|
||||||
|
static void unsafe_free(ralloc_header *info);
|
||||||
|
|
||||||
|
static ralloc_header *
|
||||||
|
get_header(const void *ptr)
|
||||||
|
{
|
||||||
|
ralloc_header *info = (ralloc_header *) (((char *) ptr) -
|
||||||
|
sizeof(ralloc_header));
|
||||||
|
assert(info->canary == CANARY);
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PTR_FROM_HEADER(info) (((char *) info) + sizeof(ralloc_header))
|
||||||
|
|
||||||
|
static void
|
||||||
|
add_child(ralloc_header *parent, ralloc_header *info)
|
||||||
|
{
|
||||||
|
if (parent != NULL) {
|
||||||
|
info->parent = parent;
|
||||||
|
info->next = parent->child;
|
||||||
|
parent->child = info;
|
||||||
|
|
||||||
|
if (info->next != NULL)
|
||||||
|
info->next->prev = info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
ralloc_context(const void *ctx)
|
||||||
|
{
|
||||||
|
return ralloc_size(ctx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
ralloc_size(const void *ctx, size_t size)
|
||||||
|
{
|
||||||
|
void *block = malloc(size + sizeof(ralloc_header));
|
||||||
|
ralloc_header *info;
|
||||||
|
ralloc_header *parent;
|
||||||
|
|
||||||
|
if (unlikely(block == NULL))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
info = (ralloc_header *) block;
|
||||||
|
/* measurements have shown that calloc is slower (because of
|
||||||
|
* the multiplication overflow checking?), so clear things
|
||||||
|
* manually
|
||||||
|
*/
|
||||||
|
info->parent = NULL;
|
||||||
|
info->child = NULL;
|
||||||
|
info->prev = NULL;
|
||||||
|
info->next = NULL;
|
||||||
|
info->destructor = NULL;
|
||||||
|
|
||||||
|
parent = ctx != NULL ? get_header(ctx) : NULL;
|
||||||
|
|
||||||
|
add_child(parent, info);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
info->canary = CANARY;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return PTR_FROM_HEADER(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
rzalloc_size(const void *ctx, size_t size)
|
||||||
|
{
|
||||||
|
void *ptr = ralloc_size(ctx, size);
|
||||||
|
|
||||||
|
if (likely(ptr))
|
||||||
|
memset(ptr, 0, size);
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper function - assumes ptr != NULL */
|
||||||
|
static void *
|
||||||
|
resize(void *ptr, size_t size)
|
||||||
|
{
|
||||||
|
ralloc_header *child, *old, *info;
|
||||||
|
|
||||||
|
old = get_header(ptr);
|
||||||
|
info = realloc(old, size + sizeof(ralloc_header));
|
||||||
|
|
||||||
|
if (info == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Update parent and sibling's links to the reallocated node. */
|
||||||
|
if (info != old && info->parent != NULL) {
|
||||||
|
if (info->parent->child == old)
|
||||||
|
info->parent->child = info;
|
||||||
|
|
||||||
|
if (info->prev != NULL)
|
||||||
|
info->prev->next = info;
|
||||||
|
|
||||||
|
if (info->next != NULL)
|
||||||
|
info->next->prev = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update child->parent links for all children */
|
||||||
|
for (child = info->child; child != NULL; child = child->next)
|
||||||
|
child->parent = info;
|
||||||
|
|
||||||
|
return PTR_FROM_HEADER(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
reralloc_size(const void *ctx, void *ptr, size_t size)
|
||||||
|
{
|
||||||
|
if (unlikely(ptr == NULL))
|
||||||
|
return ralloc_size(ctx, size);
|
||||||
|
|
||||||
|
assert(ralloc_parent(ptr) == ctx);
|
||||||
|
return resize(ptr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
rerzalloc_size(const void *ctx, void *ptr, size_t old_size, size_t new_size)
|
||||||
|
{
|
||||||
|
if (unlikely(ptr == NULL))
|
||||||
|
return rzalloc_size(ctx, new_size);
|
||||||
|
|
||||||
|
assert(ralloc_parent(ptr) == ctx);
|
||||||
|
ptr = resize(ptr, new_size);
|
||||||
|
|
||||||
|
if (new_size > old_size)
|
||||||
|
memset((char *)ptr + old_size, 0, new_size - old_size);
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
ralloc_array_size(const void *ctx, size_t size, unsigned count)
|
||||||
|
{
|
||||||
|
if (count > SIZE_MAX/size)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return ralloc_size(ctx, size * count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
rzalloc_array_size(const void *ctx, size_t size, unsigned count)
|
||||||
|
{
|
||||||
|
if (count > SIZE_MAX/size)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return rzalloc_size(ctx, size * count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
reralloc_array_size(const void *ctx, void *ptr, size_t size, unsigned count)
|
||||||
|
{
|
||||||
|
if (count > SIZE_MAX/size)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return reralloc_size(ctx, ptr, size * count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
rerzalloc_array_size(const void *ctx, void *ptr, size_t size,
|
||||||
|
unsigned old_count, unsigned new_count)
|
||||||
|
{
|
||||||
|
if (new_count > SIZE_MAX/size)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return rerzalloc_size(ctx, ptr, size * old_count, size * new_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ralloc_free(void *ptr)
|
||||||
|
{
|
||||||
|
ralloc_header *info;
|
||||||
|
|
||||||
|
if (ptr == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
info = get_header(ptr);
|
||||||
|
unlink_block(info);
|
||||||
|
unsafe_free(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
unlink_block(ralloc_header *info)
|
||||||
|
{
|
||||||
|
/* Unlink from parent & siblings */
|
||||||
|
if (info->parent != NULL) {
|
||||||
|
if (info->parent->child == info)
|
||||||
|
info->parent->child = info->next;
|
||||||
|
|
||||||
|
if (info->prev != NULL)
|
||||||
|
info->prev->next = info->next;
|
||||||
|
|
||||||
|
if (info->next != NULL)
|
||||||
|
info->next->prev = info->prev;
|
||||||
|
}
|
||||||
|
info->parent = NULL;
|
||||||
|
info->prev = NULL;
|
||||||
|
info->next = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
unsafe_free(ralloc_header *info)
|
||||||
|
{
|
||||||
|
/* Recursively free any children...don't waste time unlinking them. */
|
||||||
|
ralloc_header *temp;
|
||||||
|
while (info->child != NULL) {
|
||||||
|
temp = info->child;
|
||||||
|
info->child = temp->next;
|
||||||
|
unsafe_free(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free the block itself. Call the destructor first, if any. */
|
||||||
|
if (info->destructor != NULL)
|
||||||
|
info->destructor(PTR_FROM_HEADER(info));
|
||||||
|
|
||||||
|
free(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ralloc_steal(const void *new_ctx, void *ptr)
|
||||||
|
{
|
||||||
|
ralloc_header *info, *parent;
|
||||||
|
|
||||||
|
if (unlikely(ptr == NULL))
|
||||||
|
return;
|
||||||
|
|
||||||
|
info = get_header(ptr);
|
||||||
|
parent = new_ctx ? get_header(new_ctx) : NULL;
|
||||||
|
|
||||||
|
unlink_block(info);
|
||||||
|
|
||||||
|
add_child(parent, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ralloc_adopt(const void *new_ctx, void *old_ctx)
|
||||||
|
{
|
||||||
|
ralloc_header *new_info, *old_info, *child;
|
||||||
|
|
||||||
|
if (unlikely(old_ctx == NULL))
|
||||||
|
return;
|
||||||
|
|
||||||
|
old_info = get_header(old_ctx);
|
||||||
|
new_info = get_header(new_ctx);
|
||||||
|
|
||||||
|
/* If there are no children, bail. */
|
||||||
|
if (unlikely(old_info->child == NULL))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Set all the children's parent to new_ctx; get a pointer to the last child. */
|
||||||
|
for (child = old_info->child; child->next != NULL; child = child->next) {
|
||||||
|
child->parent = new_info;
|
||||||
|
}
|
||||||
|
child->parent = new_info;
|
||||||
|
|
||||||
|
/* Connect the two lists together; parent them to new_ctx; make old_ctx empty. */
|
||||||
|
child->next = new_info->child;
|
||||||
|
if (child->next)
|
||||||
|
child->next->prev = child;
|
||||||
|
new_info->child = old_info->child;
|
||||||
|
old_info->child = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
ralloc_parent(const void *ptr)
|
||||||
|
{
|
||||||
|
ralloc_header *info;
|
||||||
|
|
||||||
|
if (unlikely(ptr == NULL))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
info = get_header(ptr);
|
||||||
|
return info->parent ? PTR_FROM_HEADER(info->parent) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ralloc_set_destructor(const void *ptr, void(*destructor)(void *))
|
||||||
|
{
|
||||||
|
ralloc_header *info = get_header(ptr);
|
||||||
|
info->destructor = destructor;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
ralloc_strdup(const void *ctx, const char *str)
|
||||||
|
{
|
||||||
|
size_t n;
|
||||||
|
char *ptr;
|
||||||
|
|
||||||
|
if (unlikely(str == NULL))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
n = strlen(str);
|
||||||
|
ptr = ralloc_array(ctx, char, n + 1);
|
||||||
|
memcpy(ptr, str, n);
|
||||||
|
ptr[n] = '\0';
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
ralloc_strndup(const void *ctx, const char *str, size_t max)
|
||||||
|
{
|
||||||
|
size_t n;
|
||||||
|
char *ptr;
|
||||||
|
|
||||||
|
if (unlikely(str == NULL))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
n = strnlen(str, max);
|
||||||
|
ptr = ralloc_array(ctx, char, n + 1);
|
||||||
|
memcpy(ptr, str, n);
|
||||||
|
ptr[n] = '\0';
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper routine for strcat/strncat - n is the exact amount to copy */
|
||||||
|
static bool
|
||||||
|
cat(char **dest, const char *str, size_t n)
|
||||||
|
{
|
||||||
|
char *both;
|
||||||
|
size_t existing_length;
|
||||||
|
assert(dest != NULL && *dest != NULL);
|
||||||
|
|
||||||
|
existing_length = strlen(*dest);
|
||||||
|
both = resize(*dest, existing_length + n + 1);
|
||||||
|
if (unlikely(both == NULL))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
memcpy(both + existing_length, str, n);
|
||||||
|
both[existing_length + n] = '\0';
|
||||||
|
|
||||||
|
*dest = both;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
ralloc_strcat(char **dest, const char *str)
|
||||||
|
{
|
||||||
|
return cat(dest, str, strlen(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ralloc_strncat(char **dest, const char *str, size_t n)
|
||||||
|
{
|
||||||
|
return cat(dest, str, strnlen(str, n));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ralloc_str_append(char **dest, const char *str,
|
||||||
|
size_t existing_length, size_t str_size)
|
||||||
|
{
|
||||||
|
char *both;
|
||||||
|
assert(dest != NULL && *dest != NULL);
|
||||||
|
|
||||||
|
both = resize(*dest, existing_length + str_size + 1);
|
||||||
|
if (unlikely(both == NULL))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
memcpy(both + existing_length, str, str_size);
|
||||||
|
both[existing_length + str_size] = '\0';
|
||||||
|
|
||||||
|
*dest = both;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
ralloc_asprintf(const void *ctx, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
char *ptr;
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
ptr = ralloc_vasprintf(ctx, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return the length of the string that would be generated by a printf-style
|
||||||
|
* format and argument list, not including the \0 byte.
|
||||||
|
*/
|
||||||
|
static size_t
|
||||||
|
printf_length(const char *fmt, va_list untouched_args)
|
||||||
|
{
|
||||||
|
int size;
|
||||||
|
char junk;
|
||||||
|
|
||||||
|
/* Make a copy of the va_list so the original caller can still use it */
|
||||||
|
va_list args;
|
||||||
|
va_copy(args, untouched_args);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
/* We need to use _vcsprintf to calculate the size as vsnprintf returns -1
|
||||||
|
* if the number of characters to write is greater than count.
|
||||||
|
*/
|
||||||
|
size = _vscprintf(fmt, args);
|
||||||
|
(void)junk;
|
||||||
|
#else
|
||||||
|
size = vsnprintf(&junk, 1, fmt, args);
|
||||||
|
#endif
|
||||||
|
assert(size >= 0);
|
||||||
|
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
ralloc_vasprintf(const void *ctx, const char *fmt, va_list args)
|
||||||
|
{
|
||||||
|
size_t size = printf_length(fmt, args) + 1;
|
||||||
|
|
||||||
|
char *ptr = ralloc_size(ctx, size);
|
||||||
|
if (ptr != NULL)
|
||||||
|
vsnprintf(ptr, size, fmt, args);
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ralloc_asprintf_append(char **str, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
bool success;
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
success = ralloc_vasprintf_append(str, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ralloc_vasprintf_append(char **str, const char *fmt, va_list args)
|
||||||
|
{
|
||||||
|
size_t existing_length;
|
||||||
|
assert(str != NULL);
|
||||||
|
existing_length = *str ? strlen(*str) : 0;
|
||||||
|
return ralloc_vasprintf_rewrite_tail(str, &existing_length, fmt, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ralloc_asprintf_rewrite_tail(char **str, size_t *start, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
bool success;
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
success = ralloc_vasprintf_rewrite_tail(str, start, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ralloc_vasprintf_rewrite_tail(char **str, size_t *start, const char *fmt,
|
||||||
|
va_list args)
|
||||||
|
{
|
||||||
|
size_t new_length;
|
||||||
|
char *ptr;
|
||||||
|
|
||||||
|
assert(str != NULL);
|
||||||
|
|
||||||
|
if (unlikely(*str == NULL)) {
|
||||||
|
// Assuming a NULL context is probably bad, but it's expected behavior.
|
||||||
|
*str = ralloc_vasprintf(NULL, fmt, args);
|
||||||
|
*start = strlen(*str);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_length = printf_length(fmt, args);
|
||||||
|
|
||||||
|
ptr = resize(*str, *start + new_length + 1);
|
||||||
|
if (unlikely(ptr == NULL))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
vsnprintf(ptr + *start, new_length + 1, fmt, args);
|
||||||
|
*str = ptr;
|
||||||
|
*start += new_length;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* Linear allocator for short-lived allocations.
|
||||||
|
***************************************************************************
|
||||||
|
*
|
||||||
|
* The allocator consists of a parent node (2K buffer), which requires
|
||||||
|
* a ralloc parent, and child nodes (allocations). Child nodes can't be freed
|
||||||
|
* directly, because the parent doesn't track them. You have to release
|
||||||
|
* the parent node in order to release all its children.
|
||||||
|
*
|
||||||
|
* The allocator uses a fixed-sized buffer with a monotonically increasing
|
||||||
|
* offset after each allocation. If the buffer is all used, another buffer
|
||||||
|
* is allocated, sharing the same ralloc parent, so all buffers are at
|
||||||
|
* the same level in the ralloc hierarchy.
|
||||||
|
*
|
||||||
|
* The linear parent node is always the first buffer and keeps track of all
|
||||||
|
* other buffers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define MIN_LINEAR_BUFSIZE 2048
|
||||||
|
#define SUBALLOC_ALIGNMENT 8
|
||||||
|
#define LMAGIC 0x87b9c7d3
|
||||||
|
|
||||||
|
struct
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
__declspec(align(8))
|
||||||
|
#elif defined(__LP64__)
|
||||||
|
__attribute__((aligned(16)))
|
||||||
|
#else
|
||||||
|
__attribute__((aligned(8)))
|
||||||
|
#endif
|
||||||
|
linear_header {
|
||||||
|
#ifndef NDEBUG
|
||||||
|
unsigned magic; /* for debugging */
|
||||||
|
#endif
|
||||||
|
unsigned offset; /* points to the first unused byte in the buffer */
|
||||||
|
unsigned size; /* size of the buffer */
|
||||||
|
void *ralloc_parent; /* new buffers will use this */
|
||||||
|
struct linear_header *next; /* next buffer if we have more */
|
||||||
|
struct linear_header *latest; /* the only buffer that has free space */
|
||||||
|
|
||||||
|
/* After this structure, the buffer begins.
|
||||||
|
* Each suballocation consists of linear_size_chunk as its header followed
|
||||||
|
* by the suballocation, so it goes:
|
||||||
|
*
|
||||||
|
* - linear_size_chunk
|
||||||
|
* - allocated space
|
||||||
|
* - linear_size_chunk
|
||||||
|
* - allocated space
|
||||||
|
* etc.
|
||||||
|
*
|
||||||
|
* linear_size_chunk is only needed by linear_realloc.
|
||||||
|
*/
|
||||||
|
};
|
||||||
|
|
||||||
|
struct linear_size_chunk {
|
||||||
|
unsigned size; /* for realloc */
|
||||||
|
unsigned _padding;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct linear_header linear_header;
|
||||||
|
typedef struct linear_size_chunk linear_size_chunk;
|
||||||
|
|
||||||
|
#define LINEAR_PARENT_TO_HEADER(parent) \
|
||||||
|
(linear_header*) \
|
||||||
|
((char*)(parent) - sizeof(linear_size_chunk) - sizeof(linear_header))
|
||||||
|
|
||||||
|
/* Allocate the linear buffer with its header. */
|
||||||
|
static linear_header *
|
||||||
|
create_linear_node(void *ralloc_ctx, unsigned min_size)
|
||||||
|
{
|
||||||
|
linear_header *node;
|
||||||
|
|
||||||
|
min_size += sizeof(linear_size_chunk);
|
||||||
|
|
||||||
|
if (likely(min_size < MIN_LINEAR_BUFSIZE))
|
||||||
|
min_size = MIN_LINEAR_BUFSIZE;
|
||||||
|
|
||||||
|
node = ralloc_size(ralloc_ctx, sizeof(linear_header) + min_size);
|
||||||
|
if (unlikely(!node))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
node->magic = LMAGIC;
|
||||||
|
#endif
|
||||||
|
node->offset = 0;
|
||||||
|
node->size = min_size;
|
||||||
|
node->ralloc_parent = ralloc_ctx;
|
||||||
|
node->next = NULL;
|
||||||
|
node->latest = node;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
linear_alloc_child(void *parent, unsigned size)
|
||||||
|
{
|
||||||
|
linear_header *first = LINEAR_PARENT_TO_HEADER(parent);
|
||||||
|
linear_header *latest = first->latest;
|
||||||
|
linear_header *new_node;
|
||||||
|
linear_size_chunk *ptr;
|
||||||
|
unsigned full_size;
|
||||||
|
|
||||||
|
assert(first->magic == LMAGIC);
|
||||||
|
assert(!latest->next);
|
||||||
|
|
||||||
|
size = ALIGN_POT(size, SUBALLOC_ALIGNMENT);
|
||||||
|
full_size = sizeof(linear_size_chunk) + size;
|
||||||
|
|
||||||
|
if (unlikely(latest->offset + full_size > latest->size)) {
|
||||||
|
/* allocate a new node */
|
||||||
|
new_node = create_linear_node(latest->ralloc_parent, size);
|
||||||
|
if (unlikely(!new_node))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
first->latest = new_node;
|
||||||
|
latest->latest = new_node;
|
||||||
|
latest->next = new_node;
|
||||||
|
latest = new_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr = (linear_size_chunk *)((char*)&latest[1] + latest->offset);
|
||||||
|
ptr->size = size;
|
||||||
|
latest->offset += full_size;
|
||||||
|
|
||||||
|
assert((uintptr_t)&ptr[1] % SUBALLOC_ALIGNMENT == 0);
|
||||||
|
return &ptr[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
linear_alloc_parent(void *ralloc_ctx, unsigned size)
|
||||||
|
{
|
||||||
|
linear_header *node;
|
||||||
|
|
||||||
|
if (unlikely(!ralloc_ctx))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
size = ALIGN_POT(size, SUBALLOC_ALIGNMENT);
|
||||||
|
|
||||||
|
node = create_linear_node(ralloc_ctx, size);
|
||||||
|
if (unlikely(!node))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return linear_alloc_child((char*)node +
|
||||||
|
sizeof(linear_header) +
|
||||||
|
sizeof(linear_size_chunk), size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
linear_zalloc_child(void *parent, unsigned size)
|
||||||
|
{
|
||||||
|
void *ptr = linear_alloc_child(parent, size);
|
||||||
|
|
||||||
|
if (likely(ptr))
|
||||||
|
memset(ptr, 0, size);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
linear_zalloc_parent(void *parent, unsigned size)
|
||||||
|
{
|
||||||
|
void *ptr = linear_alloc_parent(parent, size);
|
||||||
|
|
||||||
|
if (likely(ptr))
|
||||||
|
memset(ptr, 0, size);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
linear_free_parent(void *ptr)
|
||||||
|
{
|
||||||
|
linear_header *node;
|
||||||
|
|
||||||
|
if (unlikely(!ptr))
|
||||||
|
return;
|
||||||
|
|
||||||
|
node = LINEAR_PARENT_TO_HEADER(ptr);
|
||||||
|
assert(node->magic == LMAGIC);
|
||||||
|
|
||||||
|
while (node) {
|
||||||
|
void *ptr = node;
|
||||||
|
|
||||||
|
node = node->next;
|
||||||
|
ralloc_free(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ralloc_steal_linear_parent(void *new_ralloc_ctx, void *ptr)
|
||||||
|
{
|
||||||
|
linear_header *node;
|
||||||
|
|
||||||
|
if (unlikely(!ptr))
|
||||||
|
return;
|
||||||
|
|
||||||
|
node = LINEAR_PARENT_TO_HEADER(ptr);
|
||||||
|
assert(node->magic == LMAGIC);
|
||||||
|
|
||||||
|
while (node) {
|
||||||
|
ralloc_steal(new_ralloc_ctx, node);
|
||||||
|
node->ralloc_parent = new_ralloc_ctx;
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
ralloc_parent_of_linear_parent(void *ptr)
|
||||||
|
{
|
||||||
|
linear_header *node = LINEAR_PARENT_TO_HEADER(ptr);
|
||||||
|
assert(node->magic == LMAGIC);
|
||||||
|
return node->ralloc_parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
linear_realloc(void *parent, void *old, unsigned new_size)
|
||||||
|
{
|
||||||
|
unsigned old_size = 0;
|
||||||
|
ralloc_header *new_ptr;
|
||||||
|
|
||||||
|
new_ptr = linear_alloc_child(parent, new_size);
|
||||||
|
|
||||||
|
if (unlikely(!old))
|
||||||
|
return new_ptr;
|
||||||
|
|
||||||
|
old_size = ((linear_size_chunk*)old)[-1].size;
|
||||||
|
|
||||||
|
if (likely(new_ptr && old_size))
|
||||||
|
memcpy(new_ptr, old, MIN2(old_size, new_size));
|
||||||
|
|
||||||
|
return new_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* All code below is pretty much copied from ralloc and only the alloc
|
||||||
|
* calls are different.
|
||||||
|
*/
|
||||||
|
|
||||||
|
char *
|
||||||
|
linear_strdup(void *parent, const char *str)
|
||||||
|
{
|
||||||
|
unsigned n;
|
||||||
|
char *ptr;
|
||||||
|
|
||||||
|
if (unlikely(!str))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
n = strlen(str);
|
||||||
|
ptr = linear_alloc_child(parent, n + 1);
|
||||||
|
if (unlikely(!ptr))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
memcpy(ptr, str, n);
|
||||||
|
ptr[n] = '\0';
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
linear_asprintf(void *parent, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
char *ptr;
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
ptr = linear_vasprintf(parent, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
linear_vasprintf(void *parent, const char *fmt, va_list args)
|
||||||
|
{
|
||||||
|
unsigned size = printf_length(fmt, args) + 1;
|
||||||
|
|
||||||
|
char *ptr = linear_alloc_child(parent, size);
|
||||||
|
if (ptr != NULL)
|
||||||
|
vsnprintf(ptr, size, fmt, args);
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
linear_asprintf_append(void *parent, char **str, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
bool success;
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
success = linear_vasprintf_append(parent, str, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
linear_vasprintf_append(void *parent, char **str, const char *fmt, va_list args)
|
||||||
|
{
|
||||||
|
size_t existing_length;
|
||||||
|
assert(str != NULL);
|
||||||
|
existing_length = *str ? strlen(*str) : 0;
|
||||||
|
return linear_vasprintf_rewrite_tail(parent, str, &existing_length, fmt, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
linear_asprintf_rewrite_tail(void *parent, char **str, size_t *start,
|
||||||
|
const char *fmt, ...)
|
||||||
|
{
|
||||||
|
bool success;
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
success = linear_vasprintf_rewrite_tail(parent, str, start, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
linear_vasprintf_rewrite_tail(void *parent, char **str, size_t *start,
|
||||||
|
const char *fmt, va_list args)
|
||||||
|
{
|
||||||
|
size_t new_length;
|
||||||
|
char *ptr;
|
||||||
|
|
||||||
|
assert(str != NULL);
|
||||||
|
|
||||||
|
if (unlikely(*str == NULL)) {
|
||||||
|
*str = linear_vasprintf(parent, fmt, args);
|
||||||
|
*start = strlen(*str);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_length = printf_length(fmt, args);
|
||||||
|
|
||||||
|
ptr = linear_realloc(parent, *str, *start + new_length + 1);
|
||||||
|
if (unlikely(ptr == NULL))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
vsnprintf(ptr + *start, new_length + 1, fmt, args);
|
||||||
|
*str = ptr;
|
||||||
|
*start += new_length;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper routine for strcat/strncat - n is the exact amount to copy */
|
||||||
|
static bool
|
||||||
|
linear_cat(void *parent, char **dest, const char *str, unsigned n)
|
||||||
|
{
|
||||||
|
char *both;
|
||||||
|
unsigned existing_length;
|
||||||
|
assert(dest != NULL && *dest != NULL);
|
||||||
|
|
||||||
|
existing_length = strlen(*dest);
|
||||||
|
both = linear_realloc(parent, *dest, existing_length + n + 1);
|
||||||
|
if (unlikely(both == NULL))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
memcpy(both + existing_length, str, n);
|
||||||
|
both[existing_length + n] = '\0';
|
||||||
|
|
||||||
|
*dest = both;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
linear_strcat(void *parent, char **dest, const char *str)
|
||||||
|
{
|
||||||
|
return linear_cat(parent, dest, str, strlen(str));
|
||||||
|
}
|
@ -0,0 +1,604 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2010 Intel Corporation
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice (including the next
|
||||||
|
* paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
* Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file ralloc.h
|
||||||
|
*
|
||||||
|
* ralloc: a recursive memory allocator
|
||||||
|
*
|
||||||
|
* The ralloc memory allocator creates a hierarchy of allocated
|
||||||
|
* objects. Every allocation is in reference to some parent, and
|
||||||
|
* every allocated object can in turn be used as the parent of a
|
||||||
|
* subsequent allocation. This allows for extremely convenient
|
||||||
|
* discarding of an entire tree/sub-tree of allocations by calling
|
||||||
|
* ralloc_free on any particular object to free it and all of its
|
||||||
|
* children.
|
||||||
|
*
|
||||||
|
* The conceptual working of ralloc was directly inspired by Andrew
|
||||||
|
* Tridgell's talloc, but ralloc is an independent implementation
|
||||||
|
* released under the MIT license and tuned for Mesa.
|
||||||
|
*
|
||||||
|
* talloc is more sophisticated than ralloc in that it includes reference
|
||||||
|
* counting and useful debugging features. However, it is released under
|
||||||
|
* a non-permissive open source license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RALLOC_H
|
||||||
|
#define RALLOC_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \def ralloc(ctx, type)
|
||||||
|
* Allocate a new object chained off of the given context.
|
||||||
|
*
|
||||||
|
* This is equivalent to:
|
||||||
|
* \code
|
||||||
|
* ((type *) ralloc_size(ctx, sizeof(type))
|
||||||
|
* \endcode
|
||||||
|
*/
|
||||||
|
#define ralloc(ctx, type) ((type *) ralloc_size(ctx, sizeof(type)))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \def rzalloc(ctx, type)
|
||||||
|
* Allocate a new object out of the given context and initialize it to zero.
|
||||||
|
*
|
||||||
|
* This is equivalent to:
|
||||||
|
* \code
|
||||||
|
* ((type *) rzalloc_size(ctx, sizeof(type))
|
||||||
|
* \endcode
|
||||||
|
*/
|
||||||
|
#define rzalloc(ctx, type) ((type *) rzalloc_size(ctx, sizeof(type)))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate a new ralloc context.
|
||||||
|
*
|
||||||
|
* While any ralloc'd pointer can be used as a context, sometimes it is useful
|
||||||
|
* to simply allocate a context with no associated memory.
|
||||||
|
*
|
||||||
|
* It is equivalent to:
|
||||||
|
* \code
|
||||||
|
* ((type *) ralloc_size(ctx, 0)
|
||||||
|
* \endcode
|
||||||
|
*/
|
||||||
|
void *ralloc_context(const void *ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate memory chained off of the given context.
|
||||||
|
*
|
||||||
|
* This is the core allocation routine which is used by all others. It
|
||||||
|
* simply allocates storage for \p size bytes and returns the pointer,
|
||||||
|
* similar to \c malloc.
|
||||||
|
*/
|
||||||
|
void *ralloc_size(const void *ctx, size_t size) MALLOCLIKE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate zero-initialized memory chained off of the given context.
|
||||||
|
*
|
||||||
|
* This is similar to \c calloc with a size of 1.
|
||||||
|
*/
|
||||||
|
void *rzalloc_size(const void *ctx, size_t size) MALLOCLIKE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resize a piece of ralloc-managed memory, preserving data.
|
||||||
|
*
|
||||||
|
* Similar to \c realloc. Unlike C89, passing 0 for \p size does not free the
|
||||||
|
* memory. Instead, it resizes it to a 0-byte ralloc context, just like
|
||||||
|
* calling ralloc_size(ctx, 0). This is different from talloc.
|
||||||
|
*
|
||||||
|
* \param ctx The context to use for new allocation. If \p ptr != NULL,
|
||||||
|
* it must be the same as ralloc_parent(\p ptr).
|
||||||
|
* \param ptr Pointer to the memory to be resized. May be NULL.
|
||||||
|
* \param size The amount of memory to allocate, in bytes.
|
||||||
|
*/
|
||||||
|
void *reralloc_size(const void *ctx, void *ptr, size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resize a ralloc-managed array, preserving data and initializing any newly
|
||||||
|
* allocated data to zero.
|
||||||
|
*
|
||||||
|
* Similar to \c realloc. Unlike C89, passing 0 for \p size does not free the
|
||||||
|
* memory. Instead, it resizes it to a 0-byte ralloc context, just like
|
||||||
|
* calling ralloc_size(ctx, 0). This is different from talloc.
|
||||||
|
*
|
||||||
|
* \param ctx The context to use for new allocation. If \p ptr != NULL,
|
||||||
|
* it must be the same as ralloc_parent(\p ptr).
|
||||||
|
* \param ptr Pointer to the memory to be resized. May be NULL.
|
||||||
|
* \param old_size The amount of memory in the previous allocation, in bytes.
|
||||||
|
* \param new_size The amount of memory to allocate, in bytes.
|
||||||
|
*/
|
||||||
|
void *rerzalloc_size(const void *ctx, void *ptr,
|
||||||
|
size_t old_size, size_t new_size);
|
||||||
|
|
||||||
|
/// \defgroup array Array Allocators @{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \def ralloc_array(ctx, type, count)
|
||||||
|
* Allocate an array of objects chained off the given context.
|
||||||
|
*
|
||||||
|
* Similar to \c calloc, but does not initialize the memory to zero.
|
||||||
|
*
|
||||||
|
* More than a convenience function, this also checks for integer overflow when
|
||||||
|
* multiplying \c sizeof(type) and \p count. This is necessary for security.
|
||||||
|
*
|
||||||
|
* This is equivalent to:
|
||||||
|
* \code
|
||||||
|
* ((type *) ralloc_array_size(ctx, sizeof(type), count)
|
||||||
|
* \endcode
|
||||||
|
*/
|
||||||
|
#define ralloc_array(ctx, type, count) \
|
||||||
|
((type *) ralloc_array_size(ctx, sizeof(type), count))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \def rzalloc_array(ctx, type, count)
|
||||||
|
* Allocate a zero-initialized array chained off the given context.
|
||||||
|
*
|
||||||
|
* Similar to \c calloc.
|
||||||
|
*
|
||||||
|
* More than a convenience function, this also checks for integer overflow when
|
||||||
|
* multiplying \c sizeof(type) and \p count. This is necessary for security.
|
||||||
|
*
|
||||||
|
* This is equivalent to:
|
||||||
|
* \code
|
||||||
|
* ((type *) rzalloc_array_size(ctx, sizeof(type), count)
|
||||||
|
* \endcode
|
||||||
|
*/
|
||||||
|
#define rzalloc_array(ctx, type, count) \
|
||||||
|
((type *) rzalloc_array_size(ctx, sizeof(type), count))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \def reralloc(ctx, ptr, type, count)
|
||||||
|
* Resize a ralloc-managed array, preserving data.
|
||||||
|
*
|
||||||
|
* Similar to \c realloc. Unlike C89, passing 0 for \p size does not free the
|
||||||
|
* memory. Instead, it resizes it to a 0-byte ralloc context, just like
|
||||||
|
* calling ralloc_size(ctx, 0). This is different from talloc.
|
||||||
|
*
|
||||||
|
* More than a convenience function, this also checks for integer overflow when
|
||||||
|
* multiplying \c sizeof(type) and \p count. This is necessary for security.
|
||||||
|
*
|
||||||
|
* \param ctx The context to use for new allocation. If \p ptr != NULL,
|
||||||
|
* it must be the same as ralloc_parent(\p ptr).
|
||||||
|
* \param ptr Pointer to the array to be resized. May be NULL.
|
||||||
|
* \param type The element type.
|
||||||
|
* \param count The number of elements to allocate.
|
||||||
|
*/
|
||||||
|
#define reralloc(ctx, ptr, type, count) \
|
||||||
|
((type *) reralloc_array_size(ctx, ptr, sizeof(type), count))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \def rerzalloc(ctx, ptr, type, count)
|
||||||
|
* Resize a ralloc-managed array, preserving data and initializing any newly
|
||||||
|
* allocated data to zero.
|
||||||
|
*
|
||||||
|
* Similar to \c realloc. Unlike C89, passing 0 for \p size does not free the
|
||||||
|
* memory. Instead, it resizes it to a 0-byte ralloc context, just like
|
||||||
|
* calling ralloc_size(ctx, 0). This is different from talloc.
|
||||||
|
*
|
||||||
|
* More than a convenience function, this also checks for integer overflow when
|
||||||
|
* multiplying \c sizeof(type) and \p count. This is necessary for security.
|
||||||
|
*
|
||||||
|
* \param ctx The context to use for new allocation. If \p ptr != NULL,
|
||||||
|
* it must be the same as ralloc_parent(\p ptr).
|
||||||
|
* \param ptr Pointer to the array to be resized. May be NULL.
|
||||||
|
* \param type The element type.
|
||||||
|
* \param old_count The number of elements in the previous allocation.
|
||||||
|
* \param new_count The number of elements to allocate.
|
||||||
|
*/
|
||||||
|
#define rerzalloc(ctx, ptr, type, old_count, new_count) \
|
||||||
|
((type *) rerzalloc_array_size(ctx, ptr, sizeof(type), old_count, new_count))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate memory for an array chained off the given context.
|
||||||
|
*
|
||||||
|
* Similar to \c calloc, but does not initialize the memory to zero.
|
||||||
|
*
|
||||||
|
* More than a convenience function, this also checks for integer overflow when
|
||||||
|
* multiplying \p size and \p count. This is necessary for security.
|
||||||
|
*/
|
||||||
|
void *ralloc_array_size(const void *ctx, size_t size, unsigned count) MALLOCLIKE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate a zero-initialized array chained off the given context.
|
||||||
|
*
|
||||||
|
* Similar to \c calloc.
|
||||||
|
*
|
||||||
|
* More than a convenience function, this also checks for integer overflow when
|
||||||
|
* multiplying \p size and \p count. This is necessary for security.
|
||||||
|
*/
|
||||||
|
void *rzalloc_array_size(const void *ctx, size_t size, unsigned count) MALLOCLIKE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resize a ralloc-managed array, preserving data.
|
||||||
|
*
|
||||||
|
* Similar to \c realloc. Unlike C89, passing 0 for \p size does not free the
|
||||||
|
* memory. Instead, it resizes it to a 0-byte ralloc context, just like
|
||||||
|
* calling ralloc_size(ctx, 0). This is different from talloc.
|
||||||
|
*
|
||||||
|
* More than a convenience function, this also checks for integer overflow when
|
||||||
|
* multiplying \c sizeof(type) and \p count. This is necessary for security.
|
||||||
|
*
|
||||||
|
* \param ctx The context to use for new allocation. If \p ptr != NULL,
|
||||||
|
* it must be the same as ralloc_parent(\p ptr).
|
||||||
|
* \param ptr Pointer to the array to be resized. May be NULL.
|
||||||
|
* \param size The size of an individual element.
|
||||||
|
* \param count The number of elements to allocate.
|
||||||
|
*
|
||||||
|
* \return True unless allocation failed.
|
||||||
|
*/
|
||||||
|
void *reralloc_array_size(const void *ctx, void *ptr, size_t size,
|
||||||
|
unsigned count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resize a ralloc-managed array, preserving data and initializing any newly
|
||||||
|
* allocated data to zero.
|
||||||
|
*
|
||||||
|
* Similar to \c realloc. Unlike C89, passing 0 for \p size does not free the
|
||||||
|
* memory. Instead, it resizes it to a 0-byte ralloc context, just like
|
||||||
|
* calling ralloc_size(ctx, 0). This is different from talloc.
|
||||||
|
*
|
||||||
|
* More than a convenience function, this also checks for integer overflow when
|
||||||
|
* multiplying \c sizeof(type) and \p count. This is necessary for security.
|
||||||
|
*
|
||||||
|
* \param ctx The context to use for new allocation. If \p ptr != NULL,
|
||||||
|
* it must be the same as ralloc_parent(\p ptr).
|
||||||
|
* \param ptr Pointer to the array to be resized. May be NULL.
|
||||||
|
* \param size The size of an individual element.
|
||||||
|
* \param old_count The number of elements in the previous allocation.
|
||||||
|
* \param new_count The number of elements to allocate.
|
||||||
|
*
|
||||||
|
* \return True unless allocation failed.
|
||||||
|
*/
|
||||||
|
void *rerzalloc_array_size(const void *ctx, void *ptr, size_t size,
|
||||||
|
unsigned old_count, unsigned new_count);
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free a piece of ralloc-managed memory.
|
||||||
|
*
|
||||||
|
* This will also free the memory of any children allocated this context.
|
||||||
|
*/
|
||||||
|
void ralloc_free(void *ptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "Steal" memory from one context, changing it to another.
|
||||||
|
*
|
||||||
|
* This changes \p ptr's context to \p new_ctx. This is quite useful if
|
||||||
|
* memory is allocated out of a temporary context.
|
||||||
|
*/
|
||||||
|
void ralloc_steal(const void *new_ctx, void *ptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reparent all children from one context to another.
|
||||||
|
*
|
||||||
|
* This effectively calls ralloc_steal(new_ctx, child) for all children of \p old_ctx.
|
||||||
|
*/
|
||||||
|
void ralloc_adopt(const void *new_ctx, void *old_ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the given pointer's ralloc context.
|
||||||
|
*/
|
||||||
|
void *ralloc_parent(const void *ptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a callback to occur just before an object is freed.
|
||||||
|
*/
|
||||||
|
void ralloc_set_destructor(const void *ptr, void(*destructor)(void *));
|
||||||
|
|
||||||
|
/// \defgroup array String Functions @{
|
||||||
|
/**
|
||||||
|
* Duplicate a string, allocating the memory from the given context.
|
||||||
|
*/
|
||||||
|
char *ralloc_strdup(const void *ctx, const char *str) MALLOCLIKE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Duplicate a string, allocating the memory from the given context.
|
||||||
|
*
|
||||||
|
* Like \c strndup, at most \p n characters are copied. If \p str is longer
|
||||||
|
* than \p n characters, \p n are copied, and a termining \c '\0' byte is added.
|
||||||
|
*/
|
||||||
|
char *ralloc_strndup(const void *ctx, const char *str, size_t n) MALLOCLIKE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Concatenate two strings, allocating the necessary space.
|
||||||
|
*
|
||||||
|
* This appends \p str to \p *dest, similar to \c strcat, using ralloc_resize
|
||||||
|
* to expand \p *dest to the appropriate size. \p dest will be updated to the
|
||||||
|
* new pointer unless allocation fails.
|
||||||
|
*
|
||||||
|
* The result will always be null-terminated.
|
||||||
|
*
|
||||||
|
* \return True unless allocation failed.
|
||||||
|
*/
|
||||||
|
bool ralloc_strcat(char **dest, const char *str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Concatenate two strings, allocating the necessary space.
|
||||||
|
*
|
||||||
|
* This appends at most \p n bytes of \p str to \p *dest, using ralloc_resize
|
||||||
|
* to expand \p *dest to the appropriate size. \p dest will be updated to the
|
||||||
|
* new pointer unless allocation fails.
|
||||||
|
*
|
||||||
|
* The result will always be null-terminated; \p str does not need to be null
|
||||||
|
* terminated if it is longer than \p n.
|
||||||
|
*
|
||||||
|
* \return True unless allocation failed.
|
||||||
|
*/
|
||||||
|
bool ralloc_strncat(char **dest, const char *str, size_t n);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Concatenate two strings, allocating the necessary space.
|
||||||
|
*
|
||||||
|
* This appends \p n bytes of \p str to \p *dest, using ralloc_resize
|
||||||
|
* to expand \p *dest to the appropriate size. \p dest will be updated to the
|
||||||
|
* new pointer unless allocation fails.
|
||||||
|
*
|
||||||
|
* The result will always be null-terminated.
|
||||||
|
*
|
||||||
|
* This function differs from ralloc_strcat() and ralloc_strncat() in that it
|
||||||
|
* does not do any strlen() calls which can become costly on large strings.
|
||||||
|
*
|
||||||
|
* \return True unless allocation failed.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
ralloc_str_append(char **dest, const char *str,
|
||||||
|
size_t existing_length, size_t str_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print to a string.
|
||||||
|
*
|
||||||
|
* This is analogous to \c sprintf, but allocates enough space (using \p ctx
|
||||||
|
* as the context) for the resulting string.
|
||||||
|
*
|
||||||
|
* \return The newly allocated string.
|
||||||
|
*/
|
||||||
|
char *ralloc_asprintf (const void *ctx, const char *fmt, ...) PRINTFLIKE(2, 3) MALLOCLIKE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print to a string, given a va_list.
|
||||||
|
*
|
||||||
|
* This is analogous to \c vsprintf, but allocates enough space (using \p ctx
|
||||||
|
* as the context) for the resulting string.
|
||||||
|
*
|
||||||
|
* \return The newly allocated string.
|
||||||
|
*/
|
||||||
|
char *ralloc_vasprintf(const void *ctx, const char *fmt, va_list args) MALLOCLIKE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rewrite the tail of an existing string, starting at a given index.
|
||||||
|
*
|
||||||
|
* Overwrites the contents of *str starting at \p start with newly formatted
|
||||||
|
* text, including a new null-terminator. Allocates more memory as necessary.
|
||||||
|
*
|
||||||
|
* This can be used to append formatted text when the length of the existing
|
||||||
|
* string is already known, saving a strlen() call.
|
||||||
|
*
|
||||||
|
* \sa ralloc_asprintf_append
|
||||||
|
*
|
||||||
|
* \param str The string to be updated.
|
||||||
|
* \param start The index to start appending new data at.
|
||||||
|
* \param fmt A printf-style formatting string
|
||||||
|
*
|
||||||
|
* \p str will be updated to the new pointer unless allocation fails.
|
||||||
|
* \p start will be increased by the length of the newly formatted text.
|
||||||
|
*
|
||||||
|
* \return True unless allocation failed.
|
||||||
|
*/
|
||||||
|
bool ralloc_asprintf_rewrite_tail(char **str, size_t *start,
|
||||||
|
const char *fmt, ...)
|
||||||
|
PRINTFLIKE(3, 4);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rewrite the tail of an existing string, starting at a given index.
|
||||||
|
*
|
||||||
|
* Overwrites the contents of *str starting at \p start with newly formatted
|
||||||
|
* text, including a new null-terminator. Allocates more memory as necessary.
|
||||||
|
*
|
||||||
|
* This can be used to append formatted text when the length of the existing
|
||||||
|
* string is already known, saving a strlen() call.
|
||||||
|
*
|
||||||
|
* \sa ralloc_vasprintf_append
|
||||||
|
*
|
||||||
|
* \param str The string to be updated.
|
||||||
|
* \param start The index to start appending new data at.
|
||||||
|
* \param fmt A printf-style formatting string
|
||||||
|
* \param args A va_list containing the data to be formatted
|
||||||
|
*
|
||||||
|
* \p str will be updated to the new pointer unless allocation fails.
|
||||||
|
* \p start will be increased by the length of the newly formatted text.
|
||||||
|
*
|
||||||
|
* \return True unless allocation failed.
|
||||||
|
*/
|
||||||
|
bool ralloc_vasprintf_rewrite_tail(char **str, size_t *start, const char *fmt,
|
||||||
|
va_list args);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append formatted text to the supplied string.
|
||||||
|
*
|
||||||
|
* This is equivalent to
|
||||||
|
* \code
|
||||||
|
* ralloc_asprintf_rewrite_tail(str, strlen(*str), fmt, ...)
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* \sa ralloc_asprintf
|
||||||
|
* \sa ralloc_asprintf_rewrite_tail
|
||||||
|
* \sa ralloc_strcat
|
||||||
|
*
|
||||||
|
* \p str will be updated to the new pointer unless allocation fails.
|
||||||
|
*
|
||||||
|
* \return True unless allocation failed.
|
||||||
|
*/
|
||||||
|
bool ralloc_asprintf_append (char **str, const char *fmt, ...)
|
||||||
|
PRINTFLIKE(2, 3);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append formatted text to the supplied string, given a va_list.
|
||||||
|
*
|
||||||
|
* This is equivalent to
|
||||||
|
* \code
|
||||||
|
* ralloc_vasprintf_rewrite_tail(str, strlen(*str), fmt, args)
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* \sa ralloc_vasprintf
|
||||||
|
* \sa ralloc_vasprintf_rewrite_tail
|
||||||
|
* \sa ralloc_strcat
|
||||||
|
*
|
||||||
|
* \p str will be updated to the new pointer unless allocation fails.
|
||||||
|
*
|
||||||
|
* \return True unless allocation failed.
|
||||||
|
*/
|
||||||
|
bool ralloc_vasprintf_append(char **str, const char *fmt, va_list args);
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Declare C++ new and delete operators which use ralloc.
|
||||||
|
*
|
||||||
|
* Placing this macro in the body of a class makes it possible to do:
|
||||||
|
*
|
||||||
|
* TYPE *var = new(mem_ctx) TYPE(...);
|
||||||
|
* delete var;
|
||||||
|
*
|
||||||
|
* which is more idiomatic in C++ than calling ralloc.
|
||||||
|
*/
|
||||||
|
#define DECLARE_ALLOC_CXX_OPERATORS_TEMPLATE(TYPE, ALLOC_FUNC) \
|
||||||
|
private: \
|
||||||
|
static void _ralloc_destructor(void *p) \
|
||||||
|
{ \
|
||||||
|
reinterpret_cast<TYPE *>(p)->TYPE::~TYPE(); \
|
||||||
|
} \
|
||||||
|
public: \
|
||||||
|
static void* operator new(size_t size, void *mem_ctx) \
|
||||||
|
{ \
|
||||||
|
void *p = ALLOC_FUNC(mem_ctx, size); \
|
||||||
|
assert(p != NULL); \
|
||||||
|
if (!HAS_TRIVIAL_DESTRUCTOR(TYPE)) \
|
||||||
|
ralloc_set_destructor(p, _ralloc_destructor); \
|
||||||
|
return p; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
static void operator delete(void *p) \
|
||||||
|
{ \
|
||||||
|
/* The object's destructor is guaranteed to have already been \
|
||||||
|
* called by the delete operator at this point -- Make sure it's \
|
||||||
|
* not called again. \
|
||||||
|
*/ \
|
||||||
|
if (!HAS_TRIVIAL_DESTRUCTOR(TYPE)) \
|
||||||
|
ralloc_set_destructor(p, NULL); \
|
||||||
|
ralloc_free(p); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DECLARE_RALLOC_CXX_OPERATORS(type) \
|
||||||
|
DECLARE_ALLOC_CXX_OPERATORS_TEMPLATE(type, ralloc_size)
|
||||||
|
|
||||||
|
#define DECLARE_RZALLOC_CXX_OPERATORS(type) \
|
||||||
|
DECLARE_ALLOC_CXX_OPERATORS_TEMPLATE(type, rzalloc_size)
|
||||||
|
|
||||||
|
#define DECLARE_LINEAR_ALLOC_CXX_OPERATORS(type) \
|
||||||
|
DECLARE_ALLOC_CXX_OPERATORS_TEMPLATE(type, linear_alloc_child)
|
||||||
|
|
||||||
|
#define DECLARE_LINEAR_ZALLOC_CXX_OPERATORS(type) \
|
||||||
|
DECLARE_ALLOC_CXX_OPERATORS_TEMPLATE(type, linear_zalloc_child)
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do a fast allocation from the linear buffer, also known as the child node
|
||||||
|
* from the allocator's point of view. It can't be freed directly. You have
|
||||||
|
* to free the parent or the ralloc parent.
|
||||||
|
*
|
||||||
|
* \param parent parent node of the linear allocator
|
||||||
|
* \param size size to allocate (max 32 bits)
|
||||||
|
*/
|
||||||
|
void *linear_alloc_child(void *parent, unsigned size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate a parent node that will hold linear buffers. The returned
|
||||||
|
* allocation is actually the first child node, but it's also the handle
|
||||||
|
* of the parent node. Use it for all child node allocations.
|
||||||
|
*
|
||||||
|
* \param ralloc_ctx ralloc context, must not be NULL
|
||||||
|
* \param size size to allocate (max 32 bits)
|
||||||
|
*/
|
||||||
|
void *linear_alloc_parent(void *ralloc_ctx, unsigned size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as linear_alloc_child, but also clears memory.
|
||||||
|
*/
|
||||||
|
void *linear_zalloc_child(void *parent, unsigned size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as linear_alloc_parent, but also clears memory.
|
||||||
|
*/
|
||||||
|
void *linear_zalloc_parent(void *ralloc_ctx, unsigned size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free the linear parent node. This will free all child nodes too.
|
||||||
|
* Freeing the ralloc parent will also free this.
|
||||||
|
*/
|
||||||
|
void linear_free_parent(void *ptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as ralloc_steal, but steals the linear parent node.
|
||||||
|
*/
|
||||||
|
void ralloc_steal_linear_parent(void *new_ralloc_ctx, void *ptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the ralloc parent of the linear parent node.
|
||||||
|
*/
|
||||||
|
void *ralloc_parent_of_linear_parent(void *ptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as realloc except that the linear allocator doesn't free child nodes,
|
||||||
|
* so it's reduced to memory duplication. It's used in places where
|
||||||
|
* reallocation is required. Don't use it often. It's much slower than
|
||||||
|
* realloc.
|
||||||
|
*/
|
||||||
|
void *linear_realloc(void *parent, void *old, unsigned new_size);
|
||||||
|
|
||||||
|
/* The functions below have the same semantics as their ralloc counterparts,
|
||||||
|
* except that they always allocate a linear child node.
|
||||||
|
*/
|
||||||
|
char *linear_strdup(void *parent, const char *str);
|
||||||
|
char *linear_asprintf(void *parent, const char *fmt, ...);
|
||||||
|
char *linear_vasprintf(void *parent, const char *fmt, va_list args);
|
||||||
|
bool linear_asprintf_append(void *parent, char **str, const char *fmt, ...);
|
||||||
|
bool linear_vasprintf_append(void *parent, char **str, const char *fmt,
|
||||||
|
va_list args);
|
||||||
|
bool linear_asprintf_rewrite_tail(void *parent, char **str, size_t *start,
|
||||||
|
const char *fmt, ...);
|
||||||
|
bool linear_vasprintf_rewrite_tail(void *parent, char **str, size_t *start,
|
||||||
|
const char *fmt, va_list args);
|
||||||
|
bool linear_strcat(void *parent, char **dest, const char *str);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* end of extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2015 Intel
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice (including the next
|
||||||
|
* paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
* Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
* IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SIMPLE_MTX_H
|
||||||
|
#define _SIMPLE_MTX_H
|
||||||
|
|
||||||
|
#include "../util/futex.h"
|
||||||
|
|
||||||
|
#include "../c11/threads.h"
|
||||||
|
|
||||||
|
#if defined(__GNUC__) && defined(HAVE_LINUX_FUTEX_H)
|
||||||
|
|
||||||
|
/* mtx_t - Fast, simple mutex
|
||||||
|
*
|
||||||
|
* While modern pthread mutexes are very fast (implemented using futex), they
|
||||||
|
* still incur a call to an external DSO and overhead of the generality and
|
||||||
|
* features of pthread mutexes. Most mutexes in mesa only needs lock/unlock,
|
||||||
|
* and the idea here is that we can inline the atomic operation and make the
|
||||||
|
* fast case just two intructions. Mutexes are subtle and finicky to
|
||||||
|
* implement, so we carefully copy the implementation from Ulrich Dreppers
|
||||||
|
* well-written and well-reviewed paper:
|
||||||
|
*
|
||||||
|
* "Futexes Are Tricky"
|
||||||
|
* http://www.akkadia.org/drepper/futex.pdf
|
||||||
|
*
|
||||||
|
* We implement "mutex3", which gives us a mutex that has no syscalls on
|
||||||
|
* uncontended lock or unlock. Further, the uncontended case boils down to a
|
||||||
|
* locked cmpxchg and an untaken branch, the uncontended unlock is just a
|
||||||
|
* locked decr and an untaken branch. We use __builtin_expect() to indicate
|
||||||
|
* that contention is unlikely so that gcc will put the contention code out of
|
||||||
|
* the main code flow.
|
||||||
|
*
|
||||||
|
* A fast mutex only supports lock/unlock, can't be recursive or used with
|
||||||
|
* condition variables.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t val;
|
||||||
|
} simple_mtx_t;
|
||||||
|
|
||||||
|
#define _SIMPLE_MTX_INITIALIZER_NP { 0 }
|
||||||
|
|
||||||
|
#define _SIMPLE_MTX_INVALID_VALUE 0xd0d0d0d0
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
simple_mtx_init(simple_mtx_t *mtx, ASSERTED int type)
|
||||||
|
{
|
||||||
|
assert(type == mtx_plain);
|
||||||
|
|
||||||
|
mtx->val = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
simple_mtx_destroy(ASSERTED simple_mtx_t *mtx)
|
||||||
|
{
|
||||||
|
#ifndef NDEBUG
|
||||||
|
mtx->val = _SIMPLE_MTX_INVALID_VALUE;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
simple_mtx_lock(simple_mtx_t *mtx)
|
||||||
|
{
|
||||||
|
uint32_t c;
|
||||||
|
|
||||||
|
c = __sync_val_compare_and_swap(&mtx->val, 0, 1);
|
||||||
|
|
||||||
|
assert(c != _SIMPLE_MTX_INVALID_VALUE);
|
||||||
|
|
||||||
|
if (__builtin_expect(c != 0, 0)) {
|
||||||
|
if (c != 2)
|
||||||
|
c = __sync_lock_test_and_set(&mtx->val, 2);
|
||||||
|
while (c != 0) {
|
||||||
|
futex_wait(&mtx->val, 2, NULL);
|
||||||
|
c = __sync_lock_test_and_set(&mtx->val, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
simple_mtx_unlock(simple_mtx_t *mtx)
|
||||||
|
{
|
||||||
|
uint32_t c;
|
||||||
|
|
||||||
|
c = __sync_fetch_and_sub(&mtx->val, 1);
|
||||||
|
|
||||||
|
assert(c != _SIMPLE_MTX_INVALID_VALUE);
|
||||||
|
|
||||||
|
if (__builtin_expect(c != 1, 0)) {
|
||||||
|
mtx->val = 0;
|
||||||
|
futex_wake(&mtx->val, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
typedef mtx_t simple_mtx_t;
|
||||||
|
|
||||||
|
#define _SIMPLE_MTX_INITIALIZER_NP _MTX_INITIALIZER_NP
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
simple_mtx_init(simple_mtx_t *mtx, int type)
|
||||||
|
{
|
||||||
|
mtx_init(mtx, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
simple_mtx_destroy(simple_mtx_t *mtx)
|
||||||
|
{
|
||||||
|
mtx_destroy(mtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
simple_mtx_lock(simple_mtx_t *mtx)
|
||||||
|
{
|
||||||
|
mtx_lock(mtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
simple_mtx_unlock(simple_mtx_t *mtx)
|
||||||
|
{
|
||||||
|
mtx_unlock(mtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,83 @@
|
|||||||
|
# Copyright © 2019 Intel Corporation
|
||||||
|
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
|
||||||
|
glslang = find_program('glslangValidator')
|
||||||
|
|
||||||
|
overlay_shaders = [
|
||||||
|
'overlay.frag',
|
||||||
|
'overlay.vert',
|
||||||
|
]
|
||||||
|
overlay_spv = []
|
||||||
|
foreach s : ['overlay.frag', 'overlay.vert']
|
||||||
|
overlay_spv += custom_target(
|
||||||
|
s + '.spv.h', input : s, output : s + '.spv.h',
|
||||||
|
command : [glslang, '-V', '-x', '-o', '@OUTPUT@', '@INPUT@'])
|
||||||
|
endforeach
|
||||||
|
|
||||||
|
vklayer_files = files(
|
||||||
|
'overlay.cpp',
|
||||||
|
'overlay_params.c',
|
||||||
|
# 'font_unispace.c',
|
||||||
|
)
|
||||||
|
|
||||||
|
vklayer_mesa_overlay = shared_library(
|
||||||
|
'MangoHud',
|
||||||
|
util_files,
|
||||||
|
vk_enum_to_str,
|
||||||
|
vklayer_files,
|
||||||
|
overlay_spv,
|
||||||
|
vk_layer_table_helpers,
|
||||||
|
c_args : [
|
||||||
|
pre_args,
|
||||||
|
c_vis_args,
|
||||||
|
no_override_init_args,
|
||||||
|
vulkan_wsi_args
|
||||||
|
],
|
||||||
|
cpp_args : [
|
||||||
|
pre_args,
|
||||||
|
cpp_vis_args,
|
||||||
|
vulkan_wsi_args
|
||||||
|
],
|
||||||
|
dependencies : [
|
||||||
|
vulkan_wsi_deps,
|
||||||
|
libimgui_core_dep,
|
||||||
|
dep_dl,
|
||||||
|
dep_pthread],
|
||||||
|
include_directories : inc_common,
|
||||||
|
link_args : cc.get_supported_link_arguments(['-Wl,-Bsymbolic-functions', '-Wl,-z,relro']),
|
||||||
|
install : true
|
||||||
|
)
|
||||||
|
|
||||||
|
install_data(
|
||||||
|
files('mangohud.json'),
|
||||||
|
install_dir : join_paths(get_option('datadir'), 'vulkan', 'implicit_layer.d'),
|
||||||
|
)
|
||||||
|
|
||||||
|
install_data(
|
||||||
|
files('setup_mangohud.sh'),
|
||||||
|
install_dir: get_option('bindir'),
|
||||||
|
)
|
||||||
|
|
||||||
|
configure_file(
|
||||||
|
input : files('mesa-overlay-control.py'),
|
||||||
|
output : '@PLAINNAME@',
|
||||||
|
configuration : configuration_data(), # only copy the file
|
||||||
|
install_dir: get_option('bindir'),
|
||||||
|
)
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,14 @@
|
|||||||
|
#version 450 core
|
||||||
|
layout(location = 0) out vec4 fColor;
|
||||||
|
|
||||||
|
layout(set=0, binding=0) uniform sampler2D sTexture;
|
||||||
|
|
||||||
|
layout(location = 0) in struct{
|
||||||
|
vec4 Color;
|
||||||
|
vec2 UV;
|
||||||
|
} In;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
fColor = In.Color * texture(sTexture, In.UV.st);
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
#version 450 core
|
||||||
|
layout(location = 0) in vec2 aPos;
|
||||||
|
layout(location = 1) in vec2 aUV;
|
||||||
|
layout(location = 2) in vec4 aColor;
|
||||||
|
|
||||||
|
layout(push_constant) uniform uPushConstant{
|
||||||
|
vec2 uScale;
|
||||||
|
vec2 uTranslate;
|
||||||
|
} pc;
|
||||||
|
|
||||||
|
out gl_PerVertex{
|
||||||
|
vec4 gl_Position;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(location = 0) out struct{
|
||||||
|
vec4 Color;
|
||||||
|
vec2 UV;
|
||||||
|
} Out;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
Out.Color = aColor;
|
||||||
|
Out.UV = aUV;
|
||||||
|
gl_Position = vec4(aPos*pc.uScale+pc.uTranslate, 0, 1);
|
||||||
|
}
|
@ -0,0 +1,223 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2019 Intel Corporation
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice (including the next
|
||||||
|
* paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
* Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
* IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/sysinfo.h>
|
||||||
|
|
||||||
|
#include "overlay_params.h"
|
||||||
|
|
||||||
|
#include "mesa/util/os_socket.h"
|
||||||
|
|
||||||
|
static enum overlay_param_position
|
||||||
|
parse_position(const char *str)
|
||||||
|
{
|
||||||
|
if (!str || !strcmp(str, "top-left"))
|
||||||
|
return LAYER_POSITION_TOP_LEFT;
|
||||||
|
if (!strcmp(str, "top-right"))
|
||||||
|
return LAYER_POSITION_TOP_RIGHT;
|
||||||
|
if (!strcmp(str, "bottom-left"))
|
||||||
|
return LAYER_POSITION_BOTTOM_LEFT;
|
||||||
|
if (!strcmp(str, "bottom-right"))
|
||||||
|
return LAYER_POSITION_BOTTOM_RIGHT;
|
||||||
|
return LAYER_POSITION_TOP_LEFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FILE *
|
||||||
|
parse_output_file(const char *str)
|
||||||
|
{
|
||||||
|
return fopen(str, "w+");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
parse_control(const char *str)
|
||||||
|
{
|
||||||
|
int ret = os_socket_listen_abstract(str, 1);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "ERROR: Couldn't create socket pipe at '%s'\n", str);
|
||||||
|
fprintf(stderr, "ERROR: '%s'\n", strerror(errno));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
os_socket_block(ret, false);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static float
|
||||||
|
parse_font_size(const char *str)
|
||||||
|
{
|
||||||
|
return strtof(str, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t
|
||||||
|
parse_fps_sampling_period(const char *str)
|
||||||
|
{
|
||||||
|
return strtol(str, NULL, 0) * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
parse_no_display(const char *str)
|
||||||
|
{
|
||||||
|
return strtol(str, NULL, 0) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned
|
||||||
|
parse_unsigned(const char *str)
|
||||||
|
{
|
||||||
|
return strtol(str, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define parse_width(s) parse_unsigned(s)
|
||||||
|
#define parse_height(s) parse_unsigned(s)
|
||||||
|
|
||||||
|
static bool
|
||||||
|
parse_help(const char *str)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Layer params using VK_LAYER_MESA_OVERLAY_CONFIG=\n");
|
||||||
|
#define OVERLAY_PARAM_BOOL(name) \
|
||||||
|
fprintf(stderr, "\t%s=0|1\n", #name);
|
||||||
|
#define OVERLAY_PARAM_CUSTOM(name)
|
||||||
|
OVERLAY_PARAMS
|
||||||
|
#undef OVERLAY_PARAM_BOOL
|
||||||
|
#undef OVERLAY_PARAM_CUSTOM
|
||||||
|
fprintf(stderr, "\tposition=top-left|top-right|bottom-left|bottom-right\n");
|
||||||
|
fprintf(stderr, "\tfps_sampling_period=number-of-milliseconds\n");
|
||||||
|
fprintf(stderr, "\tno_display=0|1\n");
|
||||||
|
fprintf(stderr, "\toutput_file=/path/to/output.txt\n");
|
||||||
|
fprintf(stderr, "\twidth=width-in-pixels\n");
|
||||||
|
fprintf(stderr, "\theight=height-in-pixels\n");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_delimiter(char c)
|
||||||
|
{
|
||||||
|
return c == 0 || c == ',' || c == ':' || c == ';' || c == '=';
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
parse_string(const char *s, char *out_param, char *out_value)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (; !is_delimiter(*s); s++, out_param++, i++)
|
||||||
|
*out_param = *s;
|
||||||
|
|
||||||
|
*out_param = 0;
|
||||||
|
|
||||||
|
if (*s == '=') {
|
||||||
|
s++;
|
||||||
|
i++;
|
||||||
|
for (; !is_delimiter(*s); s++, out_value++, i++)
|
||||||
|
*out_value = *s;
|
||||||
|
} else
|
||||||
|
*(out_value++) = '1';
|
||||||
|
*out_value = 0;
|
||||||
|
|
||||||
|
if (*s && is_delimiter(*s)) {
|
||||||
|
s++;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*s && !i) {
|
||||||
|
fprintf(stderr, "mesa-overlay: syntax error: unexpected '%c' (%i) while "
|
||||||
|
"parsing a string\n", *s, *s);
|
||||||
|
fflush(stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *overlay_param_names[] = {
|
||||||
|
#define OVERLAY_PARAM_BOOL(name) #name,
|
||||||
|
#define OVERLAY_PARAM_CUSTOM(name)
|
||||||
|
OVERLAY_PARAMS
|
||||||
|
#undef OVERLAY_PARAM_BOOL
|
||||||
|
#undef OVERLAY_PARAM_CUSTOM
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
parse_overlay_env(struct overlay_params *params,
|
||||||
|
const char *env)
|
||||||
|
{
|
||||||
|
uint32_t num;
|
||||||
|
char key[256], value[256];
|
||||||
|
|
||||||
|
memset(params, 0, sizeof(*params));
|
||||||
|
|
||||||
|
/* Visible by default */
|
||||||
|
params->enabled[OVERLAY_PARAM_ENABLED_fps] = true;
|
||||||
|
params->enabled[OVERLAY_PARAM_ENABLED_frame_timing] = true;
|
||||||
|
params->enabled[OVERLAY_PARAM_ENABLED_core_load] = false;
|
||||||
|
params->enabled[OVERLAY_PARAM_ENABLED_cpu_temp] = false;
|
||||||
|
params->enabled[OVERLAY_PARAM_ENABLED_gpu_temp] = false;
|
||||||
|
params->fps_sampling_period = 500000; /* 500ms */
|
||||||
|
params->width = 280;
|
||||||
|
params->height = 140;
|
||||||
|
params->control = -1;
|
||||||
|
|
||||||
|
if (!env)
|
||||||
|
return;
|
||||||
|
|
||||||
|
while ((num = parse_string(env, key, value)) != 0) {
|
||||||
|
env += num;
|
||||||
|
|
||||||
|
#define OVERLAY_PARAM_BOOL(name) \
|
||||||
|
if (!strcmp(#name, key)) { \
|
||||||
|
params->enabled[OVERLAY_PARAM_ENABLED_##name] = \
|
||||||
|
strtol(value, NULL, 0); \
|
||||||
|
continue; \
|
||||||
|
}
|
||||||
|
#define OVERLAY_PARAM_CUSTOM(name) \
|
||||||
|
if (!strcmp(#name, key)) { \
|
||||||
|
params->name = parse_##name(value); \
|
||||||
|
continue; \
|
||||||
|
}
|
||||||
|
OVERLAY_PARAMS
|
||||||
|
#undef OVERLAY_PARAM_BOOL
|
||||||
|
#undef OVERLAY_PARAM_CUSTOM
|
||||||
|
fprintf(stderr, "Unknown option '%s'\n", key);
|
||||||
|
}
|
||||||
|
// if font_size is used and height has not been changed from default
|
||||||
|
// increase height as needed based on font_size
|
||||||
|
bool heightChanged = false;
|
||||||
|
|
||||||
|
if (params->height != 140)
|
||||||
|
heightChanged = true;
|
||||||
|
|
||||||
|
int FrameTimeGraphHeight = 50;
|
||||||
|
|
||||||
|
if (!params->font_size)
|
||||||
|
params->font_size = 24.0f;
|
||||||
|
|
||||||
|
if (params->font_size && !heightChanged)
|
||||||
|
params->height = (params->font_size + 3 * 2) * 3 + FrameTimeGraphHeight;
|
||||||
|
|
||||||
|
// Apply more hud height if cores are enabled
|
||||||
|
if (params->enabled[OVERLAY_PARAM_ENABLED_core_load] && !heightChanged)
|
||||||
|
params->height += ((params->font_size - 3) * get_nprocs());
|
||||||
|
}
|
@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2019 Intel Corporation
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice (including the next
|
||||||
|
* paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
* Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
* IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef OVERLAY_PARAMS_H
|
||||||
|
#define OVERLAY_PARAMS_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define OVERLAY_PARAMS \
|
||||||
|
OVERLAY_PARAM_BOOL(fps) \
|
||||||
|
OVERLAY_PARAM_BOOL(frame) \
|
||||||
|
OVERLAY_PARAM_BOOL(frame_timing) \
|
||||||
|
OVERLAY_PARAM_BOOL(submit) \
|
||||||
|
OVERLAY_PARAM_BOOL(draw) \
|
||||||
|
OVERLAY_PARAM_BOOL(draw_indexed) \
|
||||||
|
OVERLAY_PARAM_BOOL(draw_indirect) \
|
||||||
|
OVERLAY_PARAM_BOOL(draw_indexed_indirect) \
|
||||||
|
OVERLAY_PARAM_BOOL(draw_indirect_count) \
|
||||||
|
OVERLAY_PARAM_BOOL(draw_indexed_indirect_count) \
|
||||||
|
OVERLAY_PARAM_BOOL(dispatch) \
|
||||||
|
OVERLAY_PARAM_BOOL(dispatch_indirect) \
|
||||||
|
OVERLAY_PARAM_BOOL(pipeline_graphics) \
|
||||||
|
OVERLAY_PARAM_BOOL(pipeline_compute) \
|
||||||
|
OVERLAY_PARAM_BOOL(pipeline_raytracing) \
|
||||||
|
OVERLAY_PARAM_BOOL(acquire) \
|
||||||
|
OVERLAY_PARAM_BOOL(acquire_timing) \
|
||||||
|
OVERLAY_PARAM_BOOL(present_timing) \
|
||||||
|
OVERLAY_PARAM_BOOL(vertices) \
|
||||||
|
OVERLAY_PARAM_BOOL(primitives) \
|
||||||
|
OVERLAY_PARAM_BOOL(vert_invocations) \
|
||||||
|
OVERLAY_PARAM_BOOL(geom_invocations) \
|
||||||
|
OVERLAY_PARAM_BOOL(geom_primitives) \
|
||||||
|
OVERLAY_PARAM_BOOL(clip_invocations) \
|
||||||
|
OVERLAY_PARAM_BOOL(clip_primitives) \
|
||||||
|
OVERLAY_PARAM_BOOL(frag_invocations) \
|
||||||
|
OVERLAY_PARAM_BOOL(tess_ctrl_patches) \
|
||||||
|
OVERLAY_PARAM_BOOL(tess_eval_invocations) \
|
||||||
|
OVERLAY_PARAM_BOOL(compute_invocations) \
|
||||||
|
OVERLAY_PARAM_BOOL(gpu_timing) \
|
||||||
|
OVERLAY_PARAM_BOOL(core_load) \
|
||||||
|
OVERLAY_PARAM_BOOL(cpu_temp) \
|
||||||
|
OVERLAY_PARAM_BOOL(gpu_temp) \
|
||||||
|
OVERLAY_PARAM_CUSTOM(fps_sampling_period) \
|
||||||
|
OVERLAY_PARAM_CUSTOM(output_file) \
|
||||||
|
OVERLAY_PARAM_CUSTOM(position) \
|
||||||
|
OVERLAY_PARAM_CUSTOM(width) \
|
||||||
|
OVERLAY_PARAM_CUSTOM(height) \
|
||||||
|
OVERLAY_PARAM_CUSTOM(no_display) \
|
||||||
|
OVERLAY_PARAM_CUSTOM(control) \
|
||||||
|
OVERLAY_PARAM_CUSTOM(font_size) \
|
||||||
|
OVERLAY_PARAM_CUSTOM(help)
|
||||||
|
|
||||||
|
enum overlay_param_position {
|
||||||
|
LAYER_POSITION_TOP_LEFT,
|
||||||
|
LAYER_POSITION_TOP_RIGHT,
|
||||||
|
LAYER_POSITION_BOTTOM_LEFT,
|
||||||
|
LAYER_POSITION_BOTTOM_RIGHT,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum overlay_param_enabled {
|
||||||
|
#define OVERLAY_PARAM_BOOL(name) OVERLAY_PARAM_ENABLED_##name,
|
||||||
|
#define OVERLAY_PARAM_CUSTOM(name)
|
||||||
|
OVERLAY_PARAMS
|
||||||
|
#undef OVERLAY_PARAM_BOOL
|
||||||
|
#undef OVERLAY_PARAM_CUSTOM
|
||||||
|
OVERLAY_PARAM_ENABLED_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
struct overlay_params {
|
||||||
|
bool enabled[OVERLAY_PARAM_ENABLED_MAX];
|
||||||
|
enum overlay_param_position position;
|
||||||
|
FILE *output_file;
|
||||||
|
int control;
|
||||||
|
uint32_t fps_sampling_period; /* us */
|
||||||
|
bool help;
|
||||||
|
bool no_display;
|
||||||
|
unsigned width;
|
||||||
|
unsigned height;
|
||||||
|
float font_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
const extern char *overlay_param_names[];
|
||||||
|
|
||||||
|
void parse_overlay_env(struct overlay_params *params,
|
||||||
|
const char *env);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* OVERLAY_PARAMS_H */
|
@ -0,0 +1,46 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
MANGOHUD_DIR=$HOME/.local/share/MangoHud/
|
||||||
|
LIB64=$HOME/.local/share/MangoHud/libMangoHud64.so
|
||||||
|
LIB32=$HOME/.local/share/MangoHud/libMangoHud32.so
|
||||||
|
IMPLICIT_LAYER_DIR=$HOME/.local/share/vulkan/implicit_layer.d
|
||||||
|
EXPLICIT_LAYER_DIR=$HOME/.local/share/vulkan/explicit_layer.d
|
||||||
|
|
||||||
|
install() {
|
||||||
|
mkdir -p $IMPLICIT_LAYER_DIR
|
||||||
|
mkdir -p $EXPLICIT_LAYER_DIR
|
||||||
|
mkdir -p $MANGOHUD_DIR
|
||||||
|
cp -v x32/libMangoHud32.so $MANGOHUD_DIR
|
||||||
|
cp -v x64/libMangoHud64.so $MANGOHUD_DIR
|
||||||
|
cp -v implicit_layer.d/mangohud32.json $IMPLICIT_LAYER_DIR
|
||||||
|
cp -v implicit_layer.d/mangohud64.json $IMPLICIT_LAYER_DIR
|
||||||
|
cp -v explicit_layer.d/mangohud32.json $EXPLICIT_LAYER_DIR
|
||||||
|
cp -v explicit_layer.d/mangohud64.json $EXPLICIT_LAYER_DIR
|
||||||
|
sed -i "s|libMangoHud.so|$LIB32|g" $IMPLICIT_LAYER_DIR/mangohud32.json
|
||||||
|
sed -i "s|libMangoHud.so|$LIB64|g" $IMPLICIT_LAYER_DIR/mangohud64.json
|
||||||
|
sed -i "s|64bit|32bit|g" $IMPLICIT_LAYER_DIR/mangohud32.json
|
||||||
|
sed -i "s|libMangoHud.so|$LIB32|g" $EXPLICIT_LAYER_DIR/mangohud32.json
|
||||||
|
sed -i "s|libMangoHud.so|$LIB64|g" $EXPLICIT_LAYER_DIR/mangohud64.json
|
||||||
|
sed -i "s|64bit|32bit|g" $EXPLICIT_LAYER_DIR/mangohud32.json
|
||||||
|
sed -i "s|mangohud|mangohud32|g" $EXPLICIT_LAYER_DIR/mangohud32.json
|
||||||
|
}
|
||||||
|
|
||||||
|
uninstall() {
|
||||||
|
rm -v $MANGOHUD_DIR/libMangoHud32.so
|
||||||
|
rm -v $MANGOHUD_DIR/libMangoHud64.so
|
||||||
|
rm -v $IMPLICIT_LAYER_DIR/mangohud32.json
|
||||||
|
rm -v $IMPLICIT_LAYER_DIR/mangohud64.json
|
||||||
|
}
|
||||||
|
|
||||||
|
case $1 in
|
||||||
|
"install")
|
||||||
|
install
|
||||||
|
;;
|
||||||
|
"uninstall")
|
||||||
|
uninstall
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unrecognized action: $1"
|
||||||
|
echo "Usage: $0 [install|uninstall]"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
Loading…
Reference in New Issue