Update Files

This commit is contained in:
2025-01-22 17:22:38 +01:00
parent 89b9349629
commit 4c5e729485
5132 changed files with 1195369 additions and 0 deletions

View File

@ -0,0 +1,25 @@
#include "audio.h"
#include <stdbool.h>
struct kinc_a1_channel {
kinc_a1_sound_t *sound;
float position;
bool loop;
volatile float volume;
float pitch;
};
struct kinc_a1_stream_channel {
kinc_a1_sound_stream_t *stream;
int position;
};
struct kinc_internal_video_channel {
struct kinc_internal_video_sound_stream *stream;
int position;
};
#include "audio.c.h"
#include "sound.c.h"
#include "soundstream.c.h"

View File

@ -0,0 +1,239 @@
#include "audio.h"
#include <stdint.h>
#include <kinc/audio2/audio.h>
#include <kinc/math/core.h>
#include <kinc/threads/atomic.h>
#include <kinc/threads/mutex.h>
#include <kinc/video.h>
#include <stdlib.h>
static kinc_mutex_t mutex;
#define CHANNEL_COUNT 16
static kinc_a1_channel_t channels[CHANNEL_COUNT];
static kinc_a1_stream_channel_t streamchannels[CHANNEL_COUNT];
static kinc_internal_video_channel_t videos[CHANNEL_COUNT];
static float sampleLinear(int16_t *data, float position) {
int pos1 = (int)position;
int pos2 = (int)(position + 1);
float sample1 = data[pos1] / 32767.0f;
float sample2 = data[pos2] / 32767.0f;
float a = position - pos1;
return sample1 * (1 - a) + sample2 * a;
}
/*float sampleHermite4pt3oX(s16* data, float position) {
float s0 = data[(int)(position - 1)] / 32767.0f;
float s1 = data[(int)(position + 0)] / 32767.0f;
float s2 = data[(int)(position + 1)] / 32767.0f;
float s3 = data[(int)(position + 2)] / 32767.0f;
float x = position - (int)(position);
// 4-point, 3rd-order Hermite (x-form)
float c0 = s1;
float c1 = 0.5f * (s2 - s0);
float c2 = s0 - 2.5f * s1 + 2 * s2 - 0.5f * s3;
float c3 = 0.5f * (s3 - s0) + 1.5f * (s1 - s2);
return ((c3 * x + c2) * x + c1) * x + c0;
}*/
void kinc_a1_mix(kinc_a2_buffer_t *buffer, int samples) {
for (int i = 0; i < samples; ++i) {
bool left = (i % 2) == 0;
float value = 0;
#if 0
__m128 sseSamples[4];
for (int i = 0; i < channelCount; i += 4) {
s16 data[4];
for (int i2 = 0; i2 < 4; ++i2) {
if (channels[i + i2].sound != nullptr) {
data[i2] = *(s16*)&channels[i + i2].sound->data[channels[i + i2].position];
channels[i + i2].position += 2;
if (channels[i + i2].position >= channels[i + i2].sound->size) channels[i + i2].sound = nullptr;
}
else {
data[i2] = 0;
}
}
sseSamples[i / 4] = _mm_set_ps(data[3] / 32767.0f, data[2] / 32767.0f, data[1] / 32767.0f, data[0] / 32767.0f);
}
__m128 a = _mm_add_ps(sseSamples[0], sseSamples[1]);
__m128 b = _mm_add_ps(sseSamples[2], sseSamples[3]);
__m128 c = _mm_add_ps(a, b);
value = c.m128_f32[0] + c.m128_f32[1] + c.m128_f32[2] + c.m128_f32[3];
value = max(min(value, 1.0f), -1.0f);
#else
kinc_mutex_lock(&mutex);
for (int i = 0; i < CHANNEL_COUNT; ++i) {
if (channels[i].sound != NULL) {
// value += *(s16*)&channels[i].sound->data[(int)channels[i].position] / 32767.0f * channels[i].sound->volume();
if (left)
value += sampleLinear(channels[i].sound->left, channels[i].position) * channels[i].volume * channels[i].volume;
else
value += sampleLinear(channels[i].sound->right, channels[i].position) * channels[i].volume * channels[i].volume;
value = kinc_max(kinc_min(value, 1.0f), -1.0f);
if (!left)
channels[i].position += channels[i].pitch / channels[i].sound->sample_rate_pos;
// channels[i].position += 2;
if (channels[i].position + 1 >= channels[i].sound->size) {
if (channels[i].loop) {
channels[i].position = 0;
}
else {
channels[i].sound = NULL;
}
}
}
}
for (int i = 0; i < CHANNEL_COUNT; ++i) {
if (streamchannels[i].stream != NULL) {
value += kinc_a1_sound_stream_next_sample(streamchannels[i].stream) * kinc_a1_sound_stream_volume(streamchannels[i].stream);
value = kinc_max(kinc_min(value, 1.0f), -1.0f);
if (kinc_a1_sound_stream_ended(streamchannels[i].stream))
streamchannels[i].stream = NULL;
}
}
for (int i = 0; i < CHANNEL_COUNT; ++i) {
if (videos[i].stream != NULL) {
value += kinc_internal_video_sound_stream_next_sample(videos[i].stream);
value = kinc_max(kinc_min(value, 1.0f), -1.0f);
if (kinc_internal_video_sound_stream_ended(videos[i].stream)) {
videos[i].stream = NULL;
}
}
}
kinc_mutex_unlock(&mutex);
#endif
*(float *)&buffer->data[buffer->write_location] = value;
buffer->write_location += 4;
if (buffer->write_location >= buffer->data_size)
buffer->write_location = 0;
}
}
void kinc_a1_init(void) {
for (int i = 0; i < CHANNEL_COUNT; ++i) {
channels[i].sound = NULL;
channels[i].position = 0;
}
for (int i = 0; i < CHANNEL_COUNT; ++i) {
streamchannels[i].stream = NULL;
streamchannels[i].position = 0;
}
kinc_mutex_init(&mutex);
kinc_a2_init();
kinc_a2_set_callback(kinc_a1_mix);
}
kinc_a1_channel_t *kinc_a1_play_sound(kinc_a1_sound_t *sound, bool loop, float pitch, bool unique) {
kinc_a1_channel_t *channel = NULL;
kinc_mutex_lock(&mutex);
bool found = false;
for (int i = 0; i < CHANNEL_COUNT; ++i) {
if (channels[i].sound == sound) {
found = true;
break;
}
}
if (!found || !unique) {
for (int i = 0; i < CHANNEL_COUNT; ++i) {
if (channels[i].sound == NULL) {
channels[i].sound = sound;
channels[i].position = 0;
channels[i].loop = loop;
channels[i].pitch = pitch;
channels[i].volume = sound->volume;
channel = &channels[i];
break;
}
}
}
kinc_mutex_unlock(&mutex);
return channel;
}
void kinc_a1_stop_sound(kinc_a1_sound_t *sound) {
kinc_mutex_lock(&mutex);
for (int i = 0; i < CHANNEL_COUNT; ++i) {
if (channels[i].sound == sound) {
channels[i].sound = NULL;
channels[i].position = 0;
break;
}
}
kinc_mutex_unlock(&mutex);
}
void kinc_a1_play_sound_stream(kinc_a1_sound_stream_t *stream) {
kinc_mutex_lock(&mutex);
for (int i = 0; i < CHANNEL_COUNT; ++i) {
if (streamchannels[i].stream == stream) {
streamchannels[i].stream = NULL;
streamchannels[i].position = 0;
break;
}
}
for (int i = 0; i < CHANNEL_COUNT; ++i) {
if (streamchannels[i].stream == NULL) {
streamchannels[i].stream = stream;
streamchannels[i].position = 0;
break;
}
}
kinc_mutex_unlock(&mutex);
}
void kinc_a1_stop_sound_stream(kinc_a1_sound_stream_t *stream) {
kinc_mutex_lock(&mutex);
for (int i = 0; i < CHANNEL_COUNT; ++i) {
if (streamchannels[i].stream == stream) {
streamchannels[i].stream = NULL;
streamchannels[i].position = 0;
break;
}
}
kinc_mutex_unlock(&mutex);
}
void kinc_internal_play_video_sound_stream(struct kinc_internal_video_sound_stream *stream) {
kinc_mutex_lock(&mutex);
for (int i = 0; i < CHANNEL_COUNT; ++i) {
if (videos[i].stream == NULL) {
videos[i].stream = stream;
videos[i].position = 0;
break;
}
}
kinc_mutex_unlock(&mutex);
}
void kinc_internal_stop_video_sound_stream(struct kinc_internal_video_sound_stream *stream) {
kinc_mutex_lock(&mutex);
for (int i = 0; i < CHANNEL_COUNT; ++i) {
if (videos[i].stream == stream) {
videos[i].stream = NULL;
videos[i].position = 0;
break;
}
}
kinc_mutex_unlock(&mutex);
}
float kinc_a1_channel_get_volume(kinc_a1_channel_t *channel) {
return channel->volume;
}
void kinc_a1_channel_set_volume(kinc_a1_channel_t *channel, float volume) {
KINC_ATOMIC_EXCHANGE_FLOAT(&channel->volume, volume);
}

View File

@ -0,0 +1,95 @@
#pragma once
#include <kinc/global.h>
#include "sound.h"
#include "soundstream.h"
#include <stdbool.h>
/*! \file audio.h
\brief Audio1 is a high-level audio-API that lets you directly play audio-files. Depending on the target-system it either sits directly on a high-level
system audio-API or is implemented based on Audio2.
*/
#ifdef __cplusplus
extern "C" {
#endif
struct kinc_internal_video_sound_stream;
struct kinc_a1_channel;
typedef struct kinc_a1_channel kinc_a1_channel_t;
struct kinc_a1_stream_channel;
typedef struct kinc_a1_stream_channel kinc_a1_stream_channel_t;
struct kinc_internal_video_channel;
typedef struct kinc_internal_video_channel kinc_internal_video_channel_t;
/// <summary>
/// Initialize the Audio1-API.
/// </summary>
KINC_FUNC void kinc_a1_init(void);
/// <summary>
/// Plays a sound immediately.
/// </summary>
/// <param name="sound">The sound to play</param>
/// <param name="loop">Whether or not to automatically loop the sound</param>
/// <param name="pitch">Changes the pitch by providing a value that's not 1.0f</param>
/// <param name="unique">Makes sure that a sound is not played more than once at the same time</param>
/// <returns>A channel object that can be used to control the playing sound. Please be aware that NULL is returned when the maximum number of simultaneously
/// played channels was reached.</returns>
KINC_FUNC kinc_a1_channel_t *kinc_a1_play_sound(kinc_a1_sound_t *sound, bool loop, float pitch, bool unique);
/// <summary>
/// Stops the sound from playing.
/// </summary>
/// <param name="sound">The sound to stop</param>
KINC_FUNC void kinc_a1_stop_sound(kinc_a1_sound_t *sound);
/// <summary>
/// Starts playing a sound-stream.
/// </summary>
/// <param name="stream">The stream to play</param>
KINC_FUNC void kinc_a1_play_sound_stream(kinc_a1_sound_stream_t *stream);
/// <summary>
/// Stops a sound-stream from playing.
/// </summary>
/// <param name="stream">The stream to stop.</param>
KINC_FUNC void kinc_a1_stop_sound_stream(kinc_a1_sound_stream_t *stream);
/// <summary>
/// Gets the current volume of a channel. Only works correctly if the channel is still playing.
/// </summary>
/// <param name="channel">The channel to get the volume from</param>
/// <returns>The volume</returns>
KINC_FUNC float kinc_a1_channel_get_volume(kinc_a1_channel_t *channel);
/// <summary>
/// Sets the current volume of a channel. Only works correctly if the channel is still playing.
/// </summary>
/// <param name="channel">The channel to set the volume for</param>
/// <param name="volume">The volume to set</param>
/// <returns></returns>
KINC_FUNC void kinc_a1_channel_set_volume(kinc_a1_channel_t *channel, float volume);
/// <summary>
/// Mixes audio into the provided buffer. kinc_a1_init sets this as the callback for a2
/// but you can also call it manually to mix a1-audio with your own audio. To do that,
/// first call kinc_a1_init, then call kinc_a2_set_callback to set it to your own callback
/// and call kinc_a1_mix from within that callback. Please be aware that the callback
/// will run in a separate audio-thread.
/// </summary>
/// <param name="buffer">The audio-buffer to be filled</param>
/// <param name="samples">The number of samples to be filled in</param>
KINC_FUNC void kinc_a1_mix(kinc_a2_buffer_t *buffer, int samples);
void kinc_internal_play_video_sound_stream(struct kinc_internal_video_sound_stream *stream);
void kinc_internal_stop_video_sound_stream(struct kinc_internal_video_sound_stream *stream);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,222 @@
#include "sound.h"
#define STB_VORBIS_HEADER_ONLY
#include <kinc/libs/stb_vorbis.c>
#include <kinc/audio2/audio.h>
#include <kinc/error.h>
#include <kinc/io/filereader.h>
#include <assert.h>
#include <string.h>
struct WaveData {
uint16_t audioFormat;
uint16_t numChannels;
uint32_t sampleRate;
uint32_t bytesPerSecond;
uint16_t bitsPerSample;
uint32_t dataSize;
uint8_t *data;
};
static void checkFOURCC(uint8_t **data, const char *fourcc) {
for (int i = 0; i < 4; ++i) {
kinc_affirm(**data == fourcc[i]);
++*data;
}
}
static void readFOURCC(uint8_t **data, char *fourcc) {
for (int i = 0; i < 4; ++i) {
fourcc[i] = **data;
++*data;
}
fourcc[4] = 0;
}
static void readChunk(uint8_t **data, struct WaveData *wave) {
char fourcc[5];
readFOURCC(data, fourcc);
uint32_t chunksize = kinc_read_u32le(*data);
*data += 4;
if (strcmp(fourcc, "fmt ") == 0) {
wave->audioFormat = kinc_read_u16le(*data + 0);
wave->numChannels = kinc_read_u16le(*data + 2);
wave->sampleRate = kinc_read_u32le(*data + 4);
wave->bytesPerSecond = kinc_read_u32le(*data + 8);
wave->bitsPerSample = kinc_read_u16le(*data + 14);
*data += chunksize;
}
else if (strcmp(fourcc, "data") == 0) {
wave->dataSize = chunksize;
wave->data = (uint8_t *)malloc(chunksize * sizeof(uint8_t));
kinc_affirm(wave->data != NULL);
memcpy(wave->data, *data, chunksize);
*data += chunksize;
}
else {
*data += chunksize;
}
}
static int16_t convert8to16(uint8_t sample) {
return (sample - 127) << 8;
}
static void splitStereo8(uint8_t *data, int size, int16_t *left, int16_t *right) {
for (int i = 0; i < size; ++i) {
left[i] = convert8to16(data[i * 2 + 0]);
right[i] = convert8to16(data[i * 2 + 1]);
}
}
static void splitStereo16(int16_t *data, int size, int16_t *left, int16_t *right) {
for (int i = 0; i < size; ++i) {
left[i] = data[i * 2 + 0];
right[i] = data[i * 2 + 1];
}
}
static void splitMono8(uint8_t *data, int size, int16_t *left, int16_t *right) {
for (int i = 0; i < size; ++i) {
left[i] = convert8to16(data[i]);
right[i] = convert8to16(data[i]);
}
}
static void splitMono16(int16_t *data, int size, int16_t *left, int16_t *right) {
for (int i = 0; i < size; ++i) {
left[i] = data[i];
right[i] = data[i];
}
}
#define MAXIMUM_SOUNDS 4096
static kinc_a1_sound_t sounds[MAXIMUM_SOUNDS] = {0};
static kinc_a1_sound_t *find_sound(void) {
for (int i = 0; i < MAXIMUM_SOUNDS; ++i) {
if (!sounds[i].in_use) {
return &sounds[i];
}
}
return NULL;
}
kinc_a1_sound_t *kinc_a1_sound_create(const char *filename) {
kinc_a1_sound_t *sound = find_sound();
assert(sound != NULL);
sound->in_use = true;
sound->volume = 1.0f;
sound->size = 0;
sound->left = NULL;
sound->right = NULL;
size_t filenameLength = strlen(filename);
uint8_t *data = NULL;
if (strncmp(&filename[filenameLength - 4], ".ogg", 4) == 0) {
kinc_file_reader_t file;
if (!kinc_file_reader_open(&file, filename, KINC_FILE_TYPE_ASSET)) {
sound->in_use = false;
return NULL;
}
uint8_t *filedata = (uint8_t *)malloc(kinc_file_reader_size(&file));
kinc_file_reader_read(&file, filedata, kinc_file_reader_size(&file));
kinc_file_reader_close(&file);
int samples =
stb_vorbis_decode_memory(filedata, (int)kinc_file_reader_size(&file), &sound->format.channels, &sound->format.samples_per_second, (short **)&data);
sound->size = samples * 2 * sound->format.channels;
sound->format.bits_per_sample = 16;
free(filedata);
}
else if (strncmp(&filename[filenameLength - 4], ".wav", 4) == 0) {
struct WaveData wave = {0};
{
kinc_file_reader_t file;
if (!kinc_file_reader_open(&file, filename, KINC_FILE_TYPE_ASSET)) {
sound->in_use = false;
return NULL;
}
uint8_t *filedata = (uint8_t *)malloc(kinc_file_reader_size(&file));
kinc_file_reader_read(&file, filedata, kinc_file_reader_size(&file));
kinc_file_reader_close(&file);
uint8_t *data = filedata;
checkFOURCC(&data, "RIFF");
uint32_t filesize = kinc_read_u32le(data);
data += 4;
checkFOURCC(&data, "WAVE");
while (data + 8 - filedata < (intptr_t)filesize) {
readChunk(&data, &wave);
}
free(filedata);
}
sound->format.bits_per_sample = wave.bitsPerSample;
sound->format.channels = wave.numChannels;
sound->format.samples_per_second = wave.sampleRate;
data = wave.data;
sound->size = wave.dataSize;
}
else {
assert(false);
}
if (sound->format.channels == 1) {
if (sound->format.bits_per_sample == 8) {
sound->left = (int16_t *)malloc(sound->size * sizeof(int16_t));
sound->right = (int16_t *)malloc(sound->size * sizeof(int16_t));
splitMono8(data, sound->size, sound->left, sound->right);
}
else if (sound->format.bits_per_sample == 16) {
sound->size /= 2;
sound->left = (int16_t *)malloc(sound->size * sizeof(int16_t));
sound->right = (int16_t *)malloc(sound->size * sizeof(int16_t));
splitMono16((int16_t *)data, sound->size, sound->left, sound->right);
}
else {
kinc_affirm(false);
}
}
else {
// Left and right channel are in s16 audio stream, alternating.
if (sound->format.bits_per_sample == 8) {
sound->size /= 2;
sound->left = (int16_t *)malloc(sound->size * sizeof(int16_t));
sound->right = (int16_t *)malloc(sound->size * sizeof(int16_t));
splitStereo8(data, sound->size, sound->left, sound->right);
}
else if (sound->format.bits_per_sample == 16) {
sound->size /= 4;
sound->left = (int16_t *)malloc(sound->size * sizeof(int16_t));
sound->right = (int16_t *)malloc(sound->size * sizeof(int16_t));
splitStereo16((int16_t *)data, sound->size, sound->left, sound->right);
}
else {
kinc_affirm(false);
}
}
sound->sample_rate_pos = 44100 / (float)sound->format.samples_per_second;
free(data);
return sound;
}
void kinc_a1_sound_destroy(kinc_a1_sound_t *sound) {
free(sound->left);
free(sound->right);
sound->left = NULL;
sound->right = NULL;
sound->in_use = false;
}
float kinc_a1_sound_volume(kinc_a1_sound_t *sound) {
return sound->volume;
}
void kinc_a1_sound_set_volume(kinc_a1_sound_t *sound, float value) {
sound->volume = value;
}

View File

@ -0,0 +1,56 @@
#pragma once
#include <kinc/global.h>
#include <kinc/audio2/audio.h>
#include <stdint.h>
/*! \file sound.h
\brief Sounds are pre-decoded on load and therefore primarily useful for playing sound-effects.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef struct kinc_a1_sound {
kinc_a2_buffer_format_t format;
int16_t *left;
int16_t *right;
int size;
float sample_rate_pos;
float volume;
bool in_use;
} kinc_a1_sound_t;
/// <summary>
/// Create a sound from a wav file.
/// </summary>
/// <param name="filename">Path to a wav file</param>
/// <returns>The newly created sound</returns>
KINC_FUNC kinc_a1_sound_t *kinc_a1_sound_create(const char *filename);
/// <summary>
/// Destroy a sound.
/// </summary>
/// <param name="sound">The sound to destroy.</param>
KINC_FUNC void kinc_a1_sound_destroy(kinc_a1_sound_t *sound);
/// <summary>
/// Gets the current volume-multiplicator that's used when playing the sound.
/// </summary>
/// <param name="sound">The sound to query</param>
/// <returns>The volume-multiplicator</returns>
KINC_FUNC float kinc_a1_sound_volume(kinc_a1_sound_t *sound);
/// <summary>
/// Sets the volume-multiplicator that's used when playing the sound.
/// </summary>
/// <param name="sound">The sound to modify</param>
/// <param name="value">The volume-multiplicator to set</param>
KINC_FUNC void kinc_a1_sound_set_volume(kinc_a1_sound_t *sound, float value);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,135 @@
#include "soundstream.h"
#define STB_VORBIS_HEADER_ONLY
#include <kinc/libs/stb_vorbis.c>
#include <kinc/io/filereader.h>
#include <stdlib.h>
#include <string.h>
static kinc_a1_sound_stream_t streams[256];
static int nextStream = 0;
static uint8_t buffer[1024 * 10];
static int bufferIndex;
kinc_a1_sound_stream_t *kinc_a1_sound_stream_create(const char *filename, bool looping) {
kinc_a1_sound_stream_t *stream = &streams[nextStream];
stream->decoded = false;
stream->myLooping = looping;
stream->myVolume = 1;
stream->rateDecodedHack = false;
stream->end = false;
kinc_file_reader_t file;
kinc_file_reader_open(&file, filename, KINC_FILE_TYPE_ASSET);
stream->buffer = &buffer[bufferIndex];
bufferIndex += (int)kinc_file_reader_size(&file);
uint8_t *filecontent = (uint8_t *)malloc(kinc_file_reader_size(&file));
kinc_file_reader_read(&file, filecontent, kinc_file_reader_size(&file));
kinc_file_reader_close(&file);
memcpy(stream->buffer, filecontent, kinc_file_reader_size(&file));
free(filecontent);
stream->vorbis = stb_vorbis_open_memory(buffer, (int)kinc_file_reader_size(&file), NULL, NULL);
if (stream->vorbis != NULL) {
stb_vorbis_info info = stb_vorbis_get_info(stream->vorbis);
stream->chans = info.channels;
stream->rate = info.sample_rate;
}
else {
stream->chans = 2;
stream->rate = 22050;
}
++nextStream;
return stream;
}
int kinc_a1_sound_stream_channels(kinc_a1_sound_stream_t *stream) {
return stream->chans;
}
int kinc_a1_sound_stream_sample_rate(kinc_a1_sound_stream_t *stream) {
return stream->rate;
}
bool kinc_a1_sound_stream_looping(kinc_a1_sound_stream_t *stream) {
return stream->myLooping;
}
void kinc_a1_sound_stream_set_looping(kinc_a1_sound_stream_t *stream, bool loop) {
stream->myLooping = loop;
}
float kinc_a1_sound_stream_volume(kinc_a1_sound_stream_t *stream) {
return stream->myVolume;
}
void kinc_a1_sound_stream_set_volume(kinc_a1_sound_stream_t *stream, float value) {
stream->myVolume = value;
}
bool kinc_a1_sound_stream_ended(kinc_a1_sound_stream_t *stream) {
return stream->end;
}
float kinc_a1_sound_stream_length(kinc_a1_sound_stream_t *stream) {
if (stream->vorbis == NULL)
return 0;
return stb_vorbis_stream_length_in_seconds(stream->vorbis);
}
float kinc_a1_sound_stream_position(kinc_a1_sound_stream_t *stream) {
if (stream->vorbis == NULL)
return 0;
return stb_vorbis_get_sample_offset(stream->vorbis) / stb_vorbis_stream_length_in_samples(stream->vorbis) * kinc_a1_sound_stream_length(stream);
}
void kinc_a1_sound_stream_reset(kinc_a1_sound_stream_t *stream) {
if (stream->vorbis != NULL)
stb_vorbis_seek_start(stream->vorbis);
stream->end = false;
stream->rateDecodedHack = false;
stream->decoded = false;
}
float kinc_a1_sound_stream_next_sample(kinc_a1_sound_stream_t *stream) {
if (stream->vorbis == NULL)
return 0;
if (stream->rate == 22050) {
if (stream->rateDecodedHack) {
if (stream->decoded) {
stream->decoded = false;
return stream->samples[0];
}
else {
stream->rateDecodedHack = false;
stream->decoded = true;
return stream->samples[1];
}
}
}
if (stream->decoded) {
stream->decoded = false;
if (stream->chans == 1) {
return stream->samples[0];
}
else {
return stream->samples[1];
}
}
else {
int read = stb_vorbis_get_samples_float_interleaved(stream->vorbis, stream->chans, &stream->samples[0], stream->chans);
if (read == 0) {
if (kinc_a1_sound_stream_looping(stream)) {
stb_vorbis_seek_start(stream->vorbis);
stb_vorbis_get_samples_float_interleaved(stream->vorbis, stream->chans, &stream->samples[0], stream->chans);
}
else {
stream->end = true;
return 0.0f;
}
}
stream->decoded = true;
stream->rateDecodedHack = true;
return stream->samples[0];
}
}

View File

@ -0,0 +1,117 @@
#pragma once
#include <kinc/global.h>
#include <stdbool.h>
#include <stdint.h>
/*! \file soundstream.h
\brief Sound-Streams are decoded while playing and as such are useful for large audio-files like music or speech.
*/
#ifdef __cplusplus
extern "C" {
#endif
struct stb_vorbis;
typedef struct kinc_a1_sound_stream {
struct stb_vorbis *vorbis;
int chans;
int rate;
bool myLooping;
float myVolume;
bool decoded;
bool rateDecodedHack;
bool end;
float samples[2];
uint8_t *buffer;
} kinc_a1_sound_stream_t;
/// <summary>
/// Create a sound-stream from a wav file.
/// </summary>
/// <param name="filename">A path to a wav file</param>
/// <param name="looping">Defines whether the stream will be looped automatically</param>
/// <returns>The newly created sound-stream</returns>
KINC_FUNC kinc_a1_sound_stream_t *kinc_a1_sound_stream_create(const char *filename, bool looping);
/// <summary>
/// Gets the next audio-sample in the stream.
/// </summary>
/// <param name="stream">The stream to extract the sample from</param>
/// <returns>The next sample</returns>
KINC_FUNC float kinc_a1_sound_stream_next_sample(kinc_a1_sound_stream_t *stream);
/// <summary>
/// Gets the number of audio-channels the stream uses.
/// </summary>
/// <param name="stream">The stream to extract the number of channels from</param>
/// <returns>The number of audio-channels</returns>
KINC_FUNC int kinc_a1_sound_stream_channels(kinc_a1_sound_stream_t *stream);
/// <summary>
/// Gets the sample-rate used by the stream.
/// </summary>
/// <param name="stream">The stream to extract the sample-rate from</param>
/// <returns>The sample-rate of the stream</returns>
KINC_FUNC int kinc_a1_sound_stream_sample_rate(kinc_a1_sound_stream_t *stream);
/// <summary>
/// Returns whether the stream loops automatically.
/// </summary>
/// <param name="stream">The stream to extract the looping-information from</param>
/// <returns>Whether the stream loops</returns>
KINC_FUNC bool kinc_a1_sound_stream_looping(kinc_a1_sound_stream_t *stream);
/// <summary>
/// Changes whether the stream is looped automatically.
/// </summary>
/// <param name="stream">The stream to change</param>
/// <param name="loop">The new loop value to set</param>
KINC_FUNC void kinc_a1_sound_stream_set_looping(kinc_a1_sound_stream_t *stream, bool loop);
/// <summary>
/// Returns whether the stream finished playing.
/// </summary>
/// <param name="stream">The stream to query</param>
/// <returns>Whether the stream finished playing</returns>
KINC_FUNC bool kinc_a1_sound_stream_ended(kinc_a1_sound_stream_t *stream);
/// <summary>
/// Returns the length of the stream.
/// </summary>
/// <param name="stream">The stream to query</param>
/// <returns>The length of the stream in seconds</returns>
KINC_FUNC float kinc_a1_sound_stream_length(kinc_a1_sound_stream_t *stream);
/// <summary>
/// Returns the current playing-position of the stream.
/// </summary>
/// <param name="stream">The stream to query</param>
/// <returns>The current playing-position in seconds</returns>
KINC_FUNC float kinc_a1_sound_stream_position(kinc_a1_sound_stream_t *stream);
/// <summary>
/// Resets the stream to its start-position.
/// </summary>
/// <param name="stream">The stream to change</param>
KINC_FUNC void kinc_a1_sound_stream_reset(kinc_a1_sound_stream_t *stream);
/// <summary>
/// Gets the stream's volume-multiplicator.
/// </summary>
/// <param name="stream">The stream to query</param>
/// <returns>The volume-multiplicator</returns>
KINC_FUNC float kinc_a1_sound_stream_volume(kinc_a1_sound_stream_t *stream);
/// <summary>
/// Sets the stream's volume-multiplicator.
/// </summary>
/// <param name="stream">The stream to change</param>
/// <param name="value">The volume-multiplicator</param>
KINC_FUNC void kinc_a1_sound_stream_set_volume(kinc_a1_sound_stream_t *stream, float value);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,3 @@
#define KINC_IMPLEMENTATION_AUDIO2
#include "audio.h"

View File

@ -0,0 +1,77 @@
#pragma once
#include <kinc/global.h>
#include <stdint.h>
/*! \file audio.h
\brief Audio2 is a low-level audio-API that allows you to directly provide a stream of audio-samples.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef struct kinc_a2_buffer_format {
int channels;
int samples_per_second;
int bits_per_sample;
} kinc_a2_buffer_format_t;
typedef struct kinc_a2_buffer {
kinc_a2_buffer_format_t format;
uint8_t *data;
int data_size;
int read_location;
int write_location;
} kinc_a2_buffer_t;
/// <summary>
/// Initializes the Audio2-API.
/// </summary>
KINC_FUNC void kinc_a2_init(void);
/// <summary>
/// Sets the callback that's used to provide audio-samples. This is the primary method of operation for Audio2. The callback is expected to write the requested
/// number of samples into the ring-buffer. The callback is typically called from the system's audio-thread to minimize audio-latency.
/// </summary>
/// <param name="kinc_a2_audio_callback">The callback to set</param>
KINC_FUNC void kinc_a2_set_callback(void (*kinc_a2_audio_callback)(kinc_a2_buffer_t *buffer, int samples));
/// <summary>
/// Sets a callback that's called when the system's sample-rate changes.
/// </summary>
/// <param name="kinc_a2_sample_rate_callback">The callback to set</param>
/// <returns></returns>
KINC_FUNC void kinc_a2_set_sample_rate_callback(void (*kinc_a2_sample_rate_callback)(void));
/// <summary>
/// The current sample-rate of the system.
/// </summary>
KINC_FUNC extern int kinc_a2_samples_per_second;
/// <summary>
/// kinc_a2_update should be called every frame. It is required by some systems to pump their audio-loops but on most systems it is a no-op.
/// </summary>
KINC_FUNC void kinc_a2_update(void);
/// <summary>
/// Shuts down the Audio2-API.
/// </summary>
KINC_FUNC void kinc_a2_shutdown(void);
#ifdef KINC_IMPLEMENTATION_AUDIO2
#define KINC_IMPLEMENTATION
#endif
#ifdef KINC_IMPLEMENTATION
int kinc_a2_samples_per_second = 44100;
// BACKENDS-PLACEHOLDER
#endif
#ifdef __cplusplus
}
#endif

46
Kinc/Sources/kinc/color.h Normal file
View File

@ -0,0 +1,46 @@
#pragma once
#include <kinc/global.h>
#include <stdint.h>
/*! \file color.h
\brief Provides some utility functionality for handling 32 bit ARGB color values.
*/
#ifdef __cplusplus
extern "C" {
#endif
/// <summary>
/// Splits up an 32 bit ARGB color value into its components.
/// </summary>
KINC_FUNC void kinc_color_components(uint32_t color, float *red, float *green, float *blue, float *alpha);
#define KINC_COLOR_BLACK 0xff000000
#define KINC_COLOR_WHITE 0xffffffff
#define KINC_COLOR_RED 0xffff0000
#define KINC_COLOR_BLUE 0xff0000ff
#define KINC_COLOR_GREEN 0xff00ff00
#define KINC_COLOR_MAGENTA 0xffff00ff
#define KINC_COLOR_YELLOW 0xffffff00
#define KINC_COLOR_CYAN 0xff00ffff
#ifdef KINC_IMPLEMENTATION_ROOT
#define KINC_IMPLEMENTATION
#endif
#ifdef KINC_IMPLEMENTATION
void kinc_color_components(uint32_t color, float *red, float *green, float *blue, float *alpha) {
*alpha = ((color & 0xff000000) >> 24) / 255.0f;
*red = ((color & 0x00ff0000) >> 16) / 255.0f;
*green = ((color & 0x0000ff00) >> 8) / 255.0f;
*blue = (color & 0x000000ff) / 255.0f;
}
#endif
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,86 @@
#pragma once
#include <kinc/global.h>
#include <stdbool.h>
/*! \file display.h
\brief Provides information for the active displays.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef struct kinc_display_mode {
int x;
int y;
int width;
int height;
int pixels_per_inch;
int frequency;
int bits_per_pixel;
} kinc_display_mode_t;
/// <summary>
/// Allows retrieval of display values prior to the kinc_init call.
/// </summary>
KINC_FUNC void kinc_display_init(void);
/// <summary>
/// Retrieves the index of the primary display.
/// </summary>
/// <returns>The index of the primary display</returns>
KINC_FUNC int kinc_primary_display(void);
/// <summary>
/// Retrieves the number of displays connected to the system.
/// </summary>
/// <remarks>
/// All indices from 0 to kinc_count_displays() - 1 are legal display indices.
/// </remarks>
/// <returns>The number of displays connected to the system</returns>
KINC_FUNC int kinc_count_displays(void);
/// <summary>
/// Checks whether the display index points to an available display.
/// </summary>
/// <param name="display_index">Index of the display to check</param>
/// <returns>
/// Returns true if the index points to an available display,
/// false otherwise
/// </returns>
KINC_FUNC bool kinc_display_available(int display_index);
/// <summary>
/// Retrieves the system name of a display.
/// </summary>
/// <param name="display_index">Index of the display to retrieve the name from</param>
/// <returns>The system name of the display</returns>
KINC_FUNC const char *kinc_display_name(int display_index);
/// <summary>
/// Retrieves the current mode of a display.
/// </summary>
/// <param name="display_index">Index of the display to retrieve the mode from</param>
/// <returns>The current display mode</returns>
KINC_FUNC kinc_display_mode_t kinc_display_current_mode(int display_index);
/// <summary>
/// Retrieves the number of available modes of a display.
/// </summary>
/// <param name="display_index">Index of the display to retrieve the modes count from</param>
/// <returns>The number of available modes of the display</returns>
KINC_FUNC int kinc_display_count_available_modes(int display_index);
/// <summary>
/// Retrieves a specific mode of a display.
/// </summary>
/// <param name="display_index">Index of the display to retrieve the mode from</param>
/// <param name="mode_index">Index of the mode to retrieve</param>
/// <returns>The display mode</returns>
KINC_FUNC kinc_display_mode_t kinc_display_available_mode(int display_index, int mode_index);
#ifdef __cplusplus
}
#endif

200
Kinc/Sources/kinc/error.h Normal file
View File

@ -0,0 +1,200 @@
#pragma once
#include <kinc/global.h>
#include <stdarg.h>
/*! \file error.h
\brief Contains functionality to stop the program in case of an error and create a user-visible error message.
The affirm and error functions print an error message and then exit the program. Error messages can be made
visible to the user (unless a console window is active this is only implemented for Windows).
*/
#ifdef __cplusplus
extern "C" {
#endif
/// <summary>
/// Exits the program when a condition is untrue and shows
/// a generic error message.
/// </summary>
/// <remarks>
/// This is an alternative to assert which also persists in release
/// builds. Use this instead of assert in situations where you want
/// your users to see what's going wrong.
/// This uses Kinc's log and error functionality to make errors
/// visible.
/// </remarks>
/// <param name="condition">
/// Exits the program if condition is false,
/// otherwise does nothing.
/// </param>
KINC_FUNC void kinc_affirm(bool condition);
/// <summary>
/// Exits the program when a condition is untrue and shows
/// a provided error message.
/// </summary>
/// <remarks>
/// This is equivalent to kinc_affirm() but uses the provided message
/// instead of a generic one.
/// </remarks>
/// <param name="condition">
/// Exits the program if condition is false,
/// otherwise does nothing.
/// </param>
/// <param name="format">
/// The parameter is equivalent to the first printf parameter.
/// </param>
/// <param name="...">
/// The parameter is equivalent to the second printf parameter.
/// </param>
KINC_FUNC void kinc_affirm_message(bool condition, const char *format, ...);
/// <summary>
/// Equivalent to kinc_affirm_message but uses a va_list parameter.
/// </summary>
/// <remarks>
/// You will need this if you want to provide the parameters using va_start/va_end.
/// </remarks>
/// <param name="condition">
/// Exits the program if condition is false,
/// otherwise does nothing.
/// </param>
/// <param name="format">
/// The parameter is equivalent to the first vprintf parameter.
/// </param>
/// <param name="...">
/// The parameter is equivalent to the second vprintf parameter.
/// </param>
KINC_FUNC void kinc_affirm_args(bool condition, const char *format, va_list args);
/// <summary>
/// Exits the program and shows a generic error message
/// </summary>
/// <remarks>
/// Mainly this just calls exit(EXIT_FAILURE) but will also use
/// Kore's log function and on Windows show an error message box.
/// </remarks>
KINC_FUNC void kinc_error(void);
/// <summary>
/// Exits the program and shows a provided error message.
/// </summary>
/// <remarks>
/// This is equivalent to kinc_error() but uses the provided message
/// instead of a generic one.
/// </remarks>
/// <param name="format">
/// The parameter is equivalent to the first printf parameter.
/// </param>
/// <param name="...">
/// The parameter is equivalent to the second printf parameter.
/// </param>
KINC_FUNC void kinc_error_message(const char *format, ...);
/// <summary>
/// Equivalent to kinc_error_message but uses a va_list parameter.
/// </summary>
/// <remarks>
/// You will need this if you want to provide the parameters using va_start/va_end.
/// </remarks>
/// <param name="format">
/// The parameter is equivalent to the first vprintf parameter.
/// </param>
/// <param name="...">
/// The parameter is equivalent to the second vprintf parameter.
/// </param>
KINC_FUNC void kinc_error_args(const char *format, va_list args);
#ifdef KINC_IMPLEMENTATION_ROOT
#define KINC_IMPLEMENTATION
#endif
#ifdef KINC_IMPLEMENTATION
#ifndef KINC_IMPLEMENTATION_ROOT
#undef KINC_IMPLEMENTATION
#endif
#include <kinc/log.h>
#ifndef KINC_IMPLEMENTATION_ROOT
#define KINC_IMPLEMENTATION
#endif
#include <stdlib.h>
#ifdef KORE_WINDOWS
#include <kinc/backend/MiniWindows.h>
#include <kinc/backend/SystemMicrosoft.h>
#endif
void kinc_affirm(bool condition) {
if (!condition) {
kinc_error();
}
}
void kinc_affirm_message(bool condition, const char *format, ...) {
if (!condition) {
va_list args;
va_start(args, format);
kinc_error_args(format, args);
va_end(args);
}
}
void kinc_affirm_args(bool condition, const char *format, va_list args) {
if (!condition) {
kinc_error_args(format, args);
}
}
void kinc_error(void) {
kinc_error_message("Unknown error");
}
void kinc_error_message(const char *format, ...) {
{
va_list args;
va_start(args, format);
kinc_log_args(KINC_LOG_LEVEL_ERROR, format, args);
va_end(args);
}
#ifdef KORE_WINDOWS
{
va_list args;
va_start(args, format);
wchar_t buffer[4096];
kinc_microsoft_format(format, args, buffer);
MessageBoxW(NULL, buffer, L"Error", 0);
va_end(args);
}
#endif
#ifndef KINC_NO_CLIB
exit(EXIT_FAILURE);
#endif
}
void kinc_error_args(const char *format, va_list args) {
kinc_log_args(KINC_LOG_LEVEL_ERROR, format, args);
#ifdef KORE_WINDOWS
wchar_t buffer[4096];
kinc_microsoft_format(format, args, buffer);
MessageBoxW(NULL, buffer, L"Error", 0);
#endif
#ifndef KINC_NO_CLIB
exit(EXIT_FAILURE);
#endif
}
#endif
#ifdef __cplusplus
}
#endif

156
Kinc/Sources/kinc/global.h Normal file
View File

@ -0,0 +1,156 @@
#pragma once
/*! \file global.h
\brief Provides basic functionality that's used all over the place. There's usually no need to manually include this.
*/
#include <stdbool.h>
#include <stdint.h>
#if defined(KORE_PPC)
#define KORE_BIG_ENDIAN
#else
#define KORE_LITTLE_ENDIAN
#endif
#if defined(KORE_PPC)
#define KINC_BIG_ENDIAN
#else
#define KINC_LITTLE_ENDIAN
#endif
#ifdef _MSC_VER
#define KINC_INLINE static __forceinline
#else
#define KINC_INLINE static __attribute__((always_inline)) inline
#endif
#ifdef _MSC_VER
#define KINC_MICROSOFT
#define KORE_MICROSOFT
#endif
#if defined(_WIN32)
#if defined(KORE_WINDOWSAPP)
#define KINC_WINDOWSAPP
#define KINC_WINRT
#define KORE_WINRT
#else
#ifndef KORE_CONSOLE
#define KINC_WINDOWS
#define KORE_WINDOWS
#endif
#endif
#elif defined(__APPLE__)
#include <TargetConditionals.h>
#if TARGET_OS_IPHONE
#if defined(KORE_TVOS)
#define KINC_TVOS
#else
#define KINC_IOS
#define KORE_IOS
#endif
#define KINC_APPLE_SOC
#else
#define KINC_MACOS
#define KORE_MACOS
#if defined(__arm64__)
#define KINC_APPLE_SOC
#endif
#endif
#define KINC_POSIX
#define KORE_POSIX
#elif defined(__linux__)
#if !defined(KORE_ANDROID)
#define KINC_LINUX
#define KORE_LINUX
#endif
#define KINC_POSIX
#define KORE_POSIX
#endif
#ifdef KORE_WINDOWS
#if defined(KINC_DYNAMIC)
#define KINC_FUNC __declspec(dllimport)
#elif defined(KINC_DYNAMIC_COMPILE)
#define KINC_FUNC __declspec(dllexport)
#else
#define KINC_FUNC
#endif
#else
#define KINC_FUNC
#endif
#ifdef __cplusplus
namespace Kore {
typedef unsigned char u8; // 1 Byte
typedef unsigned short u16; // 2 Byte
typedef unsigned int u32; // 4 Byte
#if defined(__LP64__) || defined(_LP64) || defined(_WIN64)
#define KORE_64
#endif
#ifdef KORE_WINDOWS
typedef unsigned __int64 u64; // 8 Byte
#else
typedef unsigned long long u64;
#endif
typedef char s8; // 1 Byte
typedef short s16; // 2 Byte
typedef int s32; // 4 Byte
#ifdef KORE_WINDOWS
typedef __int64 s64; // 8 Byte
#else
typedef long long s64;
#endif
typedef u32 uint; // 4 Byte
typedef s32 sint; // 4 Byte
#ifdef KORE_64
typedef s64 spint;
typedef u64 upint;
#else
typedef s32 spint;
typedef u32 upint;
#endif
}
// pseudo C++11
#if !defined(_MSC_VER) && __cplusplus <= 199711L
#define nullptr 0
#define override
#endif
#define Noexcept throw()
#endif
#ifdef __cplusplus
extern "C" {
#endif
int kickstart(int argc, char **argv);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,175 @@
#include "graphics.h"
#include <kinc/graphics4/graphics.h>
#include <kinc/graphics4/indexbuffer.h>
#include <kinc/graphics4/pipeline.h>
#include <kinc/graphics4/shader.h>
#include <kinc/graphics4/texture.h>
#include <kinc/graphics4/vertexbuffer.h>
#include <kinc/io/filereader.h>
#ifdef KINC_KONG
#include <kong.h>
#endif
#ifndef KINC_KONG
static kinc_g4_shader_t vertexShader;
static kinc_g4_shader_t fragmentShader;
static kinc_g4_pipeline_t pipeline;
static kinc_g4_texture_unit_t tex;
#endif
static kinc_g4_vertex_buffer_t vb;
static kinc_g4_index_buffer_t ib;
static kinc_g4_texture_t texture;
uint32_t *kinc_internal_g1_image;
int kinc_internal_g1_w, kinc_internal_g1_h, kinc_internal_g1_tex_width;
void kinc_g1_begin(void) {
kinc_g4_begin(0);
kinc_internal_g1_image = (uint32_t *)kinc_g4_texture_lock(&texture);
}
void kinc_g1_end(void) {
kinc_internal_g1_image = NULL;
kinc_g4_texture_unlock(&texture);
kinc_g4_clear(KINC_G4_CLEAR_COLOR, 0xff000000, 0.0f, 0);
#ifdef KINC_KONG
kinc_g4_set_pipeline(&kinc_g1_pipeline);
#else
kinc_g4_set_pipeline(&pipeline);
#endif
#ifndef KINC_KONG
kinc_g4_set_texture(tex, &texture);
#endif
kinc_g4_set_vertex_buffer(&vb);
kinc_g4_set_index_buffer(&ib);
kinc_g4_draw_indexed_vertices();
kinc_g4_end(0);
kinc_g4_swap_buffers();
}
void kinc_g1_init(int width, int height) {
kinc_internal_g1_w = width;
kinc_internal_g1_h = height;
#ifndef KINC_KONG
{
kinc_file_reader_t file;
kinc_file_reader_open(&file, "g1.vert", KINC_FILE_TYPE_ASSET);
void *data = malloc(kinc_file_reader_size(&file));
kinc_file_reader_read(&file, data, kinc_file_reader_size(&file));
kinc_file_reader_close(&file);
kinc_g4_shader_init(&vertexShader, data, kinc_file_reader_size(&file), KINC_G4_SHADER_TYPE_VERTEX);
free(data);
}
{
kinc_file_reader_t file;
kinc_file_reader_open(&file, "g1.frag", KINC_FILE_TYPE_ASSET);
void *data = malloc(kinc_file_reader_size(&file));
kinc_file_reader_read(&file, data, kinc_file_reader_size(&file));
kinc_file_reader_close(&file);
kinc_g4_shader_init(&fragmentShader, data, kinc_file_reader_size(&file), KINC_G4_SHADER_TYPE_FRAGMENT);
free(data);
}
kinc_g4_vertex_structure_t structure;
kinc_g4_vertex_structure_init(&structure);
kinc_g4_vertex_structure_add(&structure, "pos", KINC_G4_VERTEX_DATA_F32_3X);
kinc_g4_vertex_structure_add(&structure, "tex", KINC_G4_VERTEX_DATA_F32_2X);
kinc_g4_pipeline_init(&pipeline);
pipeline.input_layout[0] = &structure;
pipeline.input_layout[1] = NULL;
pipeline.vertex_shader = &vertexShader;
pipeline.fragment_shader = &fragmentShader;
kinc_g4_pipeline_compile(&pipeline);
tex = kinc_g4_pipeline_get_texture_unit(&pipeline, "texy");
#endif
kinc_g4_texture_init(&texture, width, height, KINC_IMAGE_FORMAT_RGBA32);
kinc_internal_g1_tex_width = texture.tex_width;
kinc_internal_g1_image = (uint32_t *)kinc_g4_texture_lock(&texture);
int stride = kinc_g4_texture_stride(&texture) / 4;
for (int y = 0; y < texture.tex_height; ++y) {
for (int x = 0; x < texture.tex_width; ++x) {
kinc_internal_g1_image[y * stride + x] = 0;
}
}
kinc_g4_texture_unlock(&texture);
// Correct for the difference between the texture's desired size and the actual power of 2 size
float xAspect = (float)width / texture.tex_width;
float yAspect = (float)height / texture.tex_height;
#ifdef KINC_KONG
kinc_g4_vertex_buffer_init(&vb, 4, &kinc_g1_vertex_in_structure, KINC_G4_USAGE_STATIC, 0);
#else
kinc_g4_vertex_buffer_init(&vb, 4, &structure, KINC_G4_USAGE_STATIC, 0);
#endif
float *v = kinc_g4_vertex_buffer_lock_all(&vb);
{
int i = 0;
v[i++] = -1;
v[i++] = 1;
v[i++] = 0.5;
v[i++] = 0;
v[i++] = 0;
v[i++] = 1;
v[i++] = 1;
v[i++] = 0.5;
v[i++] = xAspect;
v[i++] = 0;
v[i++] = 1;
v[i++] = -1;
v[i++] = 0.5;
v[i++] = xAspect;
v[i++] = yAspect;
v[i++] = -1;
v[i++] = -1;
v[i++] = 0.5;
v[i++] = 0;
v[i++] = yAspect;
}
kinc_g4_vertex_buffer_unlock_all(&vb);
kinc_g4_index_buffer_init(&ib, 6, KINC_G4_INDEX_BUFFER_FORMAT_32BIT, KINC_G4_USAGE_STATIC);
uint32_t *ii = (uint32_t *)kinc_g4_index_buffer_lock_all(&ib);
{
int i = 0;
ii[i++] = 0;
ii[i++] = 1;
ii[i++] = 3;
ii[i++] = 1;
ii[i++] = 2;
ii[i++] = 3;
}
kinc_g4_index_buffer_unlock_all(&ib);
}
#if defined(KINC_DYNAMIC_COMPILE) || defined(KINC_DYNAMIC)
void kinc_g1_set_pixel(int x, int y, float red, float green, float blue) {
if (x < 0 || x >= kinc_internal_g1_w || y < 0 || y >= kinc_internal_g1_h)
return;
int r = (int)(red * 255);
int g = (int)(green * 255);
int b = (int)(blue * 255);
kinc_internal_g1_image[y * kinc_internal_g1_tex_width + x] = 0xff << 24 | b << 16 | g << 8 | r;
}
int kinc_g1_width() {
return kinc_internal_g1_w;
}
int kinc_g1_height() {
return kinc_internal_g1_h;
}
#endif

View File

@ -0,0 +1,86 @@
#pragma once
#include <kinc/global.h>
#include <kinc/color.h>
#include <assert.h>
/*! \file graphics.h
\brief Supports a very basic pixel-setting API.
*/
#ifdef __cplusplus
extern "C" {
#endif
/// <summary>
/// Initializes the G1-API.
/// </summary>
/// <param name="width">The width to be used by the G1-API - typically the window-width</param>
/// <param name="height">The height to be used by the G1-API - typically the window-height</param>
KINC_FUNC void kinc_g1_init(int width, int height);
/// <summary>
/// Typically called once per frame before other G1-functions are called.
/// </summary>
KINC_FUNC void kinc_g1_begin(void);
/// <summary>
/// Typically called once per frame after all G1-drawing is finished. This also swaps the framebuffers
/// so an equivalent call to kinc_g4_swap_buffers is not needed.
/// </summary>
KINC_FUNC void kinc_g1_end(void);
extern uint32_t *kinc_internal_g1_image;
extern int kinc_internal_g1_w, kinc_internal_g1_h, kinc_internal_g1_tex_width;
#if defined(KINC_DYNAMIC_COMPILE) || defined(KINC_DYNAMIC) || defined(KINC_DOCS)
/// <summary>
/// Sets a single pixel to a color.
/// </summary>
/// <param name="x">The x-component of the pixel-coordinate to set</param>
/// <param name="y">The y-component of the pixel-coordinate to set</param>
/// <param name="red">The red-component between 0 and 1</param>
/// <param name="green">The green-component between 0 and 1</param>
/// <param name="blue">The blue-component between 0 and 1</param>
KINC_FUNC void kinc_g1_set_pixel(int x, int y, float red, float green, float blue);
/// <summary>
/// Returns the width used by G1.
/// </summary>
/// <returns>The width</returns>
KINC_FUNC int kinc_g1_width(void);
/// <summary>
/// Returns the height used by G1.
/// </summary>
/// <returns>The height</returns>
KINC_FUNC int kinc_g1_height(void);
#else
// implementation moved to the header to allow easy inlining
static inline void kinc_g1_set_pixel(int x, int y, float red, float green, float blue) {
assert(x >= 0 && x < kinc_internal_g1_w && y >= 0 && y < kinc_internal_g1_h);
int r = (int)(red * 255);
int g = (int)(green * 255);
int b = (int)(blue * 255);
kinc_internal_g1_image[y * kinc_internal_g1_tex_width + x] = 0xff << 24 | b << 16 | g << 8 | r;
}
static inline int kinc_g1_width(void) {
return kinc_internal_g1_w;
}
static inline int kinc_g1_height(void) {
return kinc_internal_g1_h;
}
#endif
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,26 @@
#pragma once
#include <kinc/global.h>
#include <kinc/graphics4/texture.h>
#include <kinc/math/matrix.h>
/*! \file graphics.h
\brief This is still in progress, please don't use it.
*/
#ifdef __cplusplus
extern "C" {
#endif
void kinc_g2_init(int screen_width, int screen_height);
void kinc_g2_begin(void);
void kinc_g2_end(void);
void kinc_g2_clear(float r, float g, float b);
void kinc_g2_draw_image(kinc_image_t *img, float x, float y);
// void drawScaledSubImage(Graphics4::Texture *img, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh);
void kinc_g2_set_rotation(float angle, float centerx, float centery);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,44 @@
#include "graphics.h"
#include <kinc/graphics1/graphics.h>
#include <kinc/math/core.h>
#include <kinc/math/matrix.h>
#include <stdint.h>
#include <string.h>
#if 0
#include <Kore/Graphics2/Graphics.h>
static Kore::Graphics2::Graphics2 *kinc_kore_g2;
static kinc_matrix3x3_t transformation;
static Kore::Graphics4::Texture *tex;
void kinc_g2_init(int screen_width, int screen_height) {
tex = new Kore::Graphics4::Texture(32, 32, Kore::Graphics1::Image::RGBA32);
kinc_kore_g2 = new Kore::Graphics2::Graphics2(screen_width, screen_height);
}
void kinc_g2_begin(void) {
kinc_kore_g2->begin();
}
void kinc_g2_end(void) {
kinc_kore_g2->end();
}
void kinc_g2_draw_image(kinc_g4_texture_t *img, float x, float y) {
tex->kincTexture = *img;
tex->width = img->tex_width;
tex->height = img->tex_height;
tex->texWidth = img->tex_width;
tex->texHeight = img->tex_height;
kinc_kore_g2->drawImage(tex, x, y);
}
void kinc_g2_set_rotation(float angle, float centerx, float centery) {
kinc_kore_g2->transformation = (Kore::mat3::Translation(centerx, centery) * Kore::mat3::RotationZ(angle)) * Kore::mat3::Translation(-centerx, -centery);
}
#endif

View File

@ -0,0 +1,285 @@
#include "graphics.h"
#include <kinc/graphics1/graphics.h>
#include <kinc/math/core.h>
#include <kinc/math/matrix.h>
#include <stdint.h>
#include <string.h>
static kinc_matrix3x3_t transform;
void kinc_g2_init(int screen_width, int screen_height) {
kinc_g1_init(screen_width, screen_height);
transform = kinc_matrix3x3_identity();
}
void kinc_g2_begin(void) {
kinc_g1_begin();
}
void kinc_g2_end(void) {
kinc_g1_end();
}
void kinc_g2_clear(float r, float g, float b) {
memset(kinc_internal_g1_image, 0, kinc_internal_g1_tex_width * kinc_internal_g1_h * 4);
}
/*void kinc_g2_draw_image(kinc_image_t *img, float x, float y) {
int xi = (int)kinc_round(x);
int yi = (int)kinc_round(y);
uint32_t *data = (uint32_t *)img->data;
for (int yy = yi; yy < yi + img->height; ++yy) {
for (int xx = xi; xx < xi + img->width; ++xx) {
uint32_t pixel = data[(yy - yi) * img->width + (xx - xi)];
uint32_t alpha = pixel >> 24;
uint32_t blue = (pixel >> 16) & 0xff;
uint32_t green = (pixel >> 8) & 0xff;
uint32_t red = pixel & 0xff;
float rf = red / 255.0f;
float gf = green / 255.0f;
float bf = blue / 255.0f;
if (alpha == 0) {
// nothing
}
else if (alpha == 255) {
kinc_g1_set_pixel(xx, yy, rf, gf, bf);
}
else {
float a = alpha / 255.0f;
uint32_t old = kinc_internal_g1_image[(yy - yi) * kinc_internal_g1_tex_width + (xx - xi)];
float oldblue = ((old >> 16) & 0xff) / 255.0f;
float oldgreen = ((old >> 8) & 0xff) / 255.0f;
float oldred = (old & 0xff) / 255.0f;
kinc_g1_set_pixel(xx, yy, rf * a + oldred * (1.0f - a), gf * a + oldgreen * (1.0f - a), bf * a + oldblue * (1.0f - a));
}
}
}
}*/
static void draw_pixel_point(kinc_image_t *img, int frame_x, int frame_y, float u, float v) {
// int xi = (int)kinc_round(x);
// int yi = (int)kinc_round(y);
uint32_t *data = (uint32_t *)img->data;
int image_x = (int)kinc_round(u * (img->width - 1));
int image_y = (int)kinc_round(v * (img->height - 1));
uint32_t pixel = data[image_y * img->width + image_x];
uint32_t alpha = pixel >> 24;
uint32_t blue = (pixel >> 16) & 0xff;
uint32_t green = (pixel >> 8) & 0xff;
uint32_t red = pixel & 0xff;
float rf = red / 255.0f;
float gf = green / 255.0f;
float bf = blue / 255.0f;
if (alpha == 0) {
// nothing
}
else if (alpha == 255) {
kinc_g1_set_pixel(frame_x, frame_y, rf, gf, bf);
}
else {
float a = alpha / 255.0f;
uint32_t old = kinc_internal_g1_image[frame_y * kinc_internal_g1_tex_width + frame_x];
float oldblue = ((old >> 16) & 0xff) / 255.0f;
float oldgreen = ((old >> 8) & 0xff) / 255.0f;
float oldred = (old & 0xff) / 255.0f;
kinc_g1_set_pixel(frame_x, frame_y, rf * a + oldred * (1.0f - a), gf * a + oldgreen * (1.0f - a), bf * a + oldblue * (1.0f - a));
}
}
#if 0
static void draw_pixel_bilinear(kinc_image_t *img, int frame_x, int frame_y, float u, float v) {
// int xi = (int)kinc_round(x);
// int yi = (int)kinc_round(y);
uint32_t *data = (uint32_t *)img->data;
int image_x0 = (int)kinc_floor(u * (img->width - 1));
int image_y0 = (int)kinc_floor(v * (img->height - 1));
int image_x1 = kinc_mini(image_x0 + 1, img->width - 1);
int image_y1 = kinc_mini(image_y0 + 1, img->height - 1);
uint32_t pixel00 = data[image_y0 * img->width + image_x0];
uint32_t pixel01 = data[image_y1 * img->width + image_x0];
uint32_t pixel10 = data[image_y0 * img->width + image_x1];
uint32_t pixel11 = data[image_y1 * img->width + image_x1];
float alpha00 = (pixel00 >> 24) / 255.0f;
float blue00 = ((pixel00 >> 16) & 0xff) / 255.0f;
float green00 = ((pixel00 >> 8) & 0xff) / 255.0f;
float red00 = (pixel00 & 0xff) / 255.0f;
float alpha01 = (pixel01 >> 24) / 255.0f;
float blue01 = ((pixel01 >> 16) & 0xff) / 255.0f;
float green01 = ((pixel01 >> 8) & 0xff) / 255.0f;
float red01 = (pixel01 & 0xff) / 255.0f;
float alpha10 = (pixel10 >> 24) / 255.0f;
float blue10 = ((pixel10 >> 16) & 0xff) / 255.0f;
float green10 = ((pixel10 >> 8) & 0xff) / 255.0f;
float red10 = (pixel10 & 0xff) / 255.0f;
float alpha11 = (pixel11 >> 24) / 255.0f;
float blue11 = ((pixel11 >> 16) & 0xff) / 255.0f;
float green11 = ((pixel11 >> 8) & 0xff) / 255.0f;
float red11 = (pixel11 & 0xff) / 255.0f;
float xx = (u * (img->width - 1)) - image_x0;
float alpha0 = alpha00 * xx + alpha10 * (1.0f - xx);
float blue0 = blue00 * xx + blue10 * (1.0f - xx);
float green0 = green00 * xx + green10 * (1.0f - xx);
float red0 = red00 * xx + red10 * (1.0f - xx);
float alpha1 = alpha01 * xx + alpha11 * (1.0f - xx);
float blue1 = blue01 * xx + blue11 * (1.0f - xx);
float green1 = green01 * xx + green11 * (1.0f - xx);
float red1 = red01 * xx + red11 * (1.0f - xx);
float yy = (v * (img->height - 1)) - image_y0;
float a = alpha0 * yy + alpha1 * (1.0f - yy);
float rf = red0 * yy + red1 * (1.0f - yy);
float gf = green0 * yy + green1 * (1.0f - yy);
float bf = blue0 * yy + blue1 * (1.0f - yy);
// float rf = red / 255.0f;
// float gf = green / 255.0f;
// float bf = blue / 255.0f;
if (a == 0.0f) {
// nothing
}
else if (a == 1.0f) {
kinc_g1_set_pixel(frame_x, frame_y, rf, gf, bf);
}
else {
// float a = alpha / 255.0f;
uint32_t old = kinc_internal_g1_image[frame_y * kinc_internal_g1_tex_width + frame_x];
float oldblue = ((old >> 16) & 0xff) / 255.0f;
float oldgreen = ((old >> 8) & 0xff) / 255.0f;
float oldred = (old & 0xff) / 255.0f;
kinc_g1_set_pixel(frame_x, frame_y, rf * a + oldred * (1.0f - a), gf * a + oldgreen * (1.0f - a), bf * a + oldblue * (1.0f - a));
}
}
#endif
static void draw_pixel(kinc_image_t *img, int frame_x, int frame_y, float u, float v) {
draw_pixel_point(img, frame_x, frame_y, u, v);
}
typedef struct Point2D {
int x, y;
} Point2D_t;
static int orient2d(Point2D_t a, Point2D_t b, Point2D_t c) {
return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
}
static int min4(int a, int b, int c, int d) {
return kinc_mini(kinc_mini(a, b), kinc_mini(c, d));
}
static int max4(int a, int b, int c, int d) {
return kinc_maxi(kinc_maxi(a, b), kinc_maxi(c, d));
}
static void drawQuad(kinc_image_t *img, Point2D_t v0, Point2D_t v1, Point2D_t v2, Point2D_t v3) {
// Compute triangle bounding box
int minX = min4(v0.x, v1.x, v2.x, v3.x);
int minY = min4(v0.y, v1.y, v2.y, v3.y);
int maxX = max4(v0.x, v1.x, v2.x, v3.x);
int maxY = max4(v0.y, v1.y, v2.y, v3.y);
// Clip against screen bounds
minX = kinc_maxi(minX, 0);
minY = kinc_maxi(minY, 0);
maxX = kinc_mini(maxX, kinc_internal_g1_w - 1);
maxY = kinc_mini(maxY, kinc_internal_g1_h - 1);
// v1 - v0
int x1 = v1.x - v0.x;
int x2 = v1.y - v0.y;
// v2 - v1
int y1 = v2.x - v1.x;
int y2 = v2.y - v1.y;
int area = x1 * y2 - x2 * y1;
// Rasterize
Point2D_t p;
for (p.y = minY; p.y <= maxY; p.y++) {
for (p.x = minX; p.x <= maxX; p.x++) {
// Determine barycentric coordinates
int w0 = orient2d(v0, v1, p);
int w1 = orient2d(v1, v2, p);
// int w2 = orient2d(v2, v3, p);
// int w3 = orient2d(v3, v0, p);
float u = w0 / (float)area; //(w0 + w2);
float v = w1 / (float)area; //(w1 + w3);
// If p is on or inside all edges, render pixel.
// if (w0 >= 0 && w1 >= 0 && w2 >= 0 && w3 >= 0) {
if (u >= 0.0f && u <= 1.0f && v >= 0.0f && v <= 1.0f) {
// renderPixel(p, w0, w1, w2);
// kinc_g1_set_pixel(p.x, p.y, u, v, 0.0f);
draw_pixel(img, p.x, p.y, u, v);
}
}
}
}
void kinc_g2_draw_image(kinc_image_t *img, float x, float y) {
kinc_vector3_t _0;
_0.x = x;
_0.y = y;
_0.z = 1.0f;
kinc_vector3_t _1;
_1.x = x + img->width;
_1.y = y;
_1.z = 1.0f;
kinc_vector3_t _2;
_2.x = x + img->width;
_2.y = y + img->height;
_2.z = 1.0f;
kinc_vector3_t _3;
_3.x = x;
_3.y = y + img->height;
_3.z = 1.0f;
_0 = kinc_matrix3x3_multiply_vector(&transform, _0);
_1 = kinc_matrix3x3_multiply_vector(&transform, _1);
_2 = kinc_matrix3x3_multiply_vector(&transform, _2);
_3 = kinc_matrix3x3_multiply_vector(&transform, _3);
Point2D_t v0, v1, v2, v3;
v0.x = (int)kinc_round(_0.x);
v0.y = (int)kinc_round(_0.y);
v1.x = (int)kinc_round(_1.x);
v1.y = (int)kinc_round(_1.y);
v2.x = (int)kinc_round(_2.x);
v2.y = (int)kinc_round(_2.y);
v3.x = (int)kinc_round(_3.x);
v3.y = (int)kinc_round(_3.y);
drawQuad(img, v0, v1, v2, v3);
}
void kinc_g2_set_rotation(float angle, float centerx, float centery) {
kinc_matrix3x3_t translation1 = kinc_matrix3x3_translation(centerx, centery);
kinc_matrix3x3_t rotation = kinc_matrix3x3_rotation_z(angle);
kinc_matrix3x3_t translation2 = kinc_matrix3x3_translation(-centerx, -centery);
kinc_matrix3x3_t transformation1 = kinc_matrix3x3_multiply(&translation1, &rotation);
transform = kinc_matrix3x3_multiply(&transformation1, &translation2);
}

View File

@ -0,0 +1,226 @@
#pragma once
#include <kinc/global.h>
#include <kinc/backend/compute.h>
#ifdef KORE_OPENGL
#include <kinc/backend/graphics4/ShaderStorageBufferImpl.h>
#endif
#include <kinc/graphics4/graphics.h>
#include <kinc/graphics4/vertexstructure.h>
#include <kinc/math/matrix.h>
/*! \file compute.h
\brief Provides support for running compute-shaders.
*/
#ifdef __cplusplus
extern "C" {
#endif
struct kinc_g4_texture;
struct kinc_g4_render_target;
typedef struct kinc_compute_constant_location {
kinc_compute_constant_location_impl_t impl;
} kinc_compute_constant_location_t;
typedef struct kinc_compute_texture_unit {
kinc_compute_texture_unit_impl_t impl;
} kinc_compute_texture_unit_t;
typedef struct kinc_compute_shader {
kinc_compute_shader_impl_t impl;
} kinc_compute_shader_t;
/// <summary>
/// Initialize a compute-shader from system-specific shader-data.
/// </summary>
/// <param name="shader">The shader-object to initialize</param>
/// <param name="source">A pointer to system-specific shader-data</param>
/// <param name="length">Length of the shader-data in bytes</param>
KINC_FUNC void kinc_compute_shader_init(kinc_compute_shader_t *shader, void *source, int length);
/// <summary>
/// Destroy a shader-object
/// </summary>
/// <param name="shader">The shader-object to destroy</param>
KINC_FUNC void kinc_compute_shader_destroy(kinc_compute_shader_t *shader);
#ifndef KINC_KONG
/// <summary>
/// Finds the location of a constant/uniform inside of a shader.
/// </summary>
/// <param name="shader">The shader to look into</param>
/// <param name="name">The constant/uniform-name to look for</param>
/// <returns>The found constant-location</returns>
KINC_FUNC kinc_compute_constant_location_t kinc_compute_shader_get_constant_location(kinc_compute_shader_t *shader, const char *name);
/// <summary>
/// Finds a texture-unit inside of a shader.
/// </summary>
/// <param name="shader">The shader to look into</param>
/// <param name="name">The texture-name to look for</param>
/// <returns>The found texture-unit</returns>
KINC_FUNC kinc_compute_texture_unit_t kinc_compute_shader_get_texture_unit(kinc_compute_shader_t *shader, const char *name);
#endif
#ifdef KORE_OPENGL
typedef struct kinc_shader_storage_buffer {
kinc_compute_shader_storage_buffer_impl_t impl;
} kinc_shader_storage_buffer_t;
KINC_FUNC void kinc_shader_storage_buffer_init(kinc_shader_storage_buffer_t *buffer, int count, kinc_g4_vertex_data_t type);
KINC_FUNC void kinc_shader_storage_buffer_destroy(kinc_shader_storage_buffer_t *buffer);
KINC_FUNC int *kinc_shader_storage_buffer_lock(kinc_shader_storage_buffer_t *buffer);
KINC_FUNC void kinc_shader_storage_buffer_unlock(kinc_shader_storage_buffer_t *buffer);
KINC_FUNC int kinc_shader_storage_buffer_count(kinc_shader_storage_buffer_t *buffer);
KINC_FUNC void kinc_shader_storage_buffer_internal_set(kinc_shader_storage_buffer_t *buffer);
#endif
typedef enum kinc_compute_access { KINC_COMPUTE_ACCESS_READ, KINC_COMPUTE_ACCESS_WRITE, KINC_COMPUTE_ACCESS_READ_WRITE } kinc_compute_access_t;
/// <summary>
/// Assigns a bool-value to a constant/uniform. The constant/uniform has to be declared as a bool in the shader.
/// </summary>
/// <param name="location">The location to set</param>
/// <param name="value">The value to set the location to</param>
KINC_FUNC void kinc_compute_set_bool(kinc_compute_constant_location_t location, bool value);
/// <summary>
/// Assigns an int-value to a constant/uniform. The constant/uniform has to be declared as an int in the shader.
/// </summary>
/// <param name="location">The location to set</param>
/// <param name="value">The value to set the location to</param>
KINC_FUNC void kinc_compute_set_int(kinc_compute_constant_location_t location, int value);
/// <summary>
/// Assigns a float-value to a constant/uniform. The constant/uniform has to be declared as a float in the shader.
/// </summary>
/// <param name="location">The location to set</param>
/// <param name="value">The value to set the location to</param>
KINC_FUNC void kinc_compute_set_float(kinc_compute_constant_location_t location, float value);
/// <summary>
/// Assigns two float-values to a constant/uniform. The constant/uniform has to be declared as a vec2 in the shader.
/// </summary>
/// <param name="location">The location to set</param>
KINC_FUNC void kinc_compute_set_float2(kinc_compute_constant_location_t location, float value1, float value2);
/// <summary>
/// Assigns three float-values to a constant/uniform. The constant/uniform has to be declared as a vec3 in the shader.
/// </summary>
/// <param name="location">The location to set</param>
KINC_FUNC void kinc_compute_set_float3(kinc_compute_constant_location_t location, float value1, float value2, float value3);
/// <summary>
/// Assigns four float-values to a constant/uniform. The constant/uniform has to be declared as a vec4 in the shader.
/// </summary>
/// <param name="location">The location to set</param>
KINC_FUNC void kinc_compute_set_float4(kinc_compute_constant_location_t location, float value1, float value2, float value3, float value4);
/// <summary>
/// Assigns an array of float-values to a constant/uniform. The constant/uniform has to be declared as a float-array in the shader.
/// </summary>
/// <param name="location">The location to set</param>
KINC_FUNC void kinc_compute_set_floats(kinc_compute_constant_location_t location, float *values, int count);
/// <summary>
/// Assigns a 4x4-matrix-value to a constant/uniform. The constant/uniform has to be declared as a mat4 in the shader.
/// </summary>
/// <param name="location">The location to set</param>
KINC_FUNC void kinc_compute_set_matrix4(kinc_compute_constant_location_t location, kinc_matrix4x4_t *value);
/// <summary>
/// Assigns a 3x3-matrix-value to a constant/uniform. The constant/uniform has to be declared as a mat3 in the shader.
/// </summary>
/// <param name="location">The location to set</param>
KINC_FUNC void kinc_compute_set_matrix3(kinc_compute_constant_location_t location, kinc_matrix3x3_t *value);
#ifdef KORE_OPENGL
KINC_FUNC void kinc_compute_set_buffer(kinc_shader_storage_buffer_t *buffer, int index);
#endif
/// <summary>
/// Assigns a texture to a texture-unit for direct access.
/// </summary>
KINC_FUNC void kinc_compute_set_texture(kinc_compute_texture_unit_t unit, struct kinc_g4_texture *texture, kinc_compute_access_t access);
/// <summary>
/// Assigns a render-target to a texture-unit for direct access.
/// </summary>
KINC_FUNC void kinc_compute_set_render_target(kinc_compute_texture_unit_t unit, struct kinc_g4_render_target *texture, kinc_compute_access_t access);
/// <summary>
/// Assigns a texture to a texture-unit for samples access.
/// </summary>
KINC_FUNC void kinc_compute_set_sampled_texture(kinc_compute_texture_unit_t unit, struct kinc_g4_texture *texture);
/// <summary>
/// Assigns a render-target to a texture-unit for samples access.
/// </summary>
KINC_FUNC void kinc_compute_set_sampled_render_target(kinc_compute_texture_unit_t unit, struct kinc_g4_render_target *target);
/// <summary>
/// Assigns the depth-component of a render-target to a texture-unit for samples access.
/// </summary>
KINC_FUNC void kinc_compute_set_sampled_depth_from_render_target(kinc_compute_texture_unit_t unit, struct kinc_g4_render_target *target);
/// <summary>
/// Assigns the mode for accessing a texture outside of the 0 to 1-range for a texture-unit.
/// </summary>
KINC_FUNC void kinc_compute_set_texture_addressing(kinc_compute_texture_unit_t unit, kinc_g4_texture_direction_t dir, kinc_g4_texture_addressing_t addressing);
/// <summary>
/// Sets the magnification-mode for a texture-unit.
/// </summary>
KINC_FUNC void kinc_compute_set_texture_magnification_filter(kinc_compute_texture_unit_t unit, kinc_g4_texture_filter_t filter);
/// <summary>
/// Sets the minification-mode for a texture-unit.
/// </summary>
KINC_FUNC void kinc_compute_set_texture_minification_filter(kinc_compute_texture_unit_t unit, kinc_g4_texture_filter_t filter);
/// <summary>
/// Sets the mipmap-mode for a texture-unit.
/// </summary>
KINC_FUNC void kinc_compute_set_texture_mipmap_filter(kinc_compute_texture_unit_t unit, kinc_g4_mipmap_filter_t filter);
/// <summary>
/// Assigns the mode for accessing a texture outside of the 0 to 1-range for a texture-unit.
/// </summary>
KINC_FUNC void kinc_compute_set_texture3d_addressing(kinc_compute_texture_unit_t unit, kinc_g4_texture_direction_t dir,
kinc_g4_texture_addressing_t addressing);
/// <summary>
/// Sets the magnification-mode for a texture-unit.
/// </summary>
KINC_FUNC void kinc_compute_set_texture3d_magnification_filter(kinc_compute_texture_unit_t unit, kinc_g4_texture_filter_t filter);
/// <summary>
/// Sets the minification-mode for a texture-unit.
/// </summary>
KINC_FUNC void kinc_compute_set_texture3d_minification_filter(kinc_compute_texture_unit_t unit, kinc_g4_texture_filter_t filter);
/// <summary>
/// Sets the mipmap-mode for a texture-unit.
/// </summary>
KINC_FUNC void kinc_compute_set_texture3d_mipmap_filter(kinc_compute_texture_unit_t unit, kinc_g4_mipmap_filter_t filter);
/// <summary>
/// Sets a shader for the next compute-run.
/// </summary>
/// <param name="shader">The shader to use</param>
KINC_FUNC void kinc_compute_set_shader(kinc_compute_shader_t *shader);
/// <summary>
/// Fire off a compute-run on x * y * z elements.
/// </summary>
/// <param name="x">The x-size for the compute-run</param>
/// <param name="y">The y-size for the compute-run</param>
/// <param name="z">The z-size for the compute-run</param>
KINC_FUNC void kinc_compute(int x, int y, int z);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,77 @@
#pragma once
#ifdef KINC_KONG
#include <kinc/global.h>
#include <kinc/backend/graphics4/constantbuffer.h>
#include <kinc/math/matrix.h>
#include <kinc/math/vector.h>
/*! \file constantbuffer.h
\brief Provides support for managing buffers of constant-data for shaders.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef struct kinc_g4_constant_buffer {
kinc_g4_constant_buffer_impl impl;
} kinc_g4_constant_buffer;
/// <summary>
/// Initializes a constant-buffer.
/// </summary>
/// <param name="buffer">The buffer to initialize</param>
/// <param name="size">The size of the constant-data in the buffer in bytes</param>
KINC_FUNC void kinc_g4_constant_buffer_init(kinc_g4_constant_buffer *buffer, size_t size);
/// <summary>
/// Destroys a buffer.
/// </summary>
/// <param name="buffer">The buffer to destroy</param>
KINC_FUNC void kinc_g4_constant_buffer_destroy(kinc_g4_constant_buffer *buffer);
/// <summary>
/// Locks all of a constant-buffer to modify its contents.
/// </summary>
/// <param name="buffer">The buffer to lock</param>
/// <returns>The contents of the buffer</returns>
KINC_FUNC uint8_t *kinc_g4_constant_buffer_lock_all(kinc_g4_constant_buffer *buffer);
/// <summary>
/// Locks part of a constant-buffer to modify its contents.
/// </summary>
/// <param name="buffer">The buffer to lock</param>
/// <param name="start">The offset of where to start the lock in bytes</param>
/// <param name="count">The number of bytes to lock</param>
/// <returns>The contents of the buffer, starting at start</returns>
KINC_FUNC uint8_t *kinc_g4_constant_buffer_lock(kinc_g4_constant_buffer *buffer, size_t start, size_t count);
/// <summary>
/// Unlocks a constant-buffer so the changed contents can be used.
/// </summary>
/// <param name="buffer">The buffer to unlock</param>
KINC_FUNC void kinc_g4_constant_buffer_unlock_all(kinc_g4_constant_buffer *buffer);
/// <summary>
/// Unlocks parts of a constant-buffer so the changed contents can be used.
/// </summary>
/// <param name="buffer">The buffer to unlock</param>
/// /// <param name="count">The number of bytes to unlock, starting from the start-index from the previous lock-call</param>
KINC_FUNC void kinc_g4_constant_buffer_unlock(kinc_g4_constant_buffer *buffer, size_t count);
/// <summary>
/// Figures out the size of the constant-data in the buffer.
/// </summary>
/// <param name="buffer">The buffer to figure out the size for</param>
/// <returns>Returns the size of the constant-data in the buffer in bytes</returns>
KINC_FUNC size_t kinc_g4_constant_buffer_size(kinc_g4_constant_buffer *buffer);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,22 @@
#pragma once
#include <kinc/global.h>
#include <kinc/backend/graphics4/pipeline.h>
#include <kinc/backend/graphics4/shader.h>
/*! \file constantlocation.h
\brief Provides the constant_location-struct which is used for setting constants/uniforms in a shader.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef struct kinc_g4_constant_location {
kinc_g4_constant_location_impl_t impl;
} kinc_g4_constant_location_t;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,4 @@
#include "graphics.c.h"
#include "pipeline.c.h"
#include "rendertarget.c.h"
#include "vertexbuffer.c.h"

View File

@ -0,0 +1,11 @@
#include "graphics.h"
static int antialiasing_samples;
int kinc_g4_antialiasing_samples(void) {
return antialiasing_samples;
}
void kinc_g4_set_antialiasing_samples(int samples) {
antialiasing_samples = samples;
}

View File

@ -0,0 +1,432 @@
#pragma once
#include <kinc/global.h>
#include <kinc/math/matrix.h>
#include "constantlocation.h"
#include "pipeline.h"
#include "textureunit.h"
/*! \file graphics.h
\brief Contains the base G4-functionality.
*/
#ifdef __cplusplus
extern "C" {
#endif
struct kinc_g4_pipeline;
struct kinc_g4_render_target;
struct kinc_g4_texture;
struct kinc_g4_texture_array;
#ifdef KINC_KONG
struct kinc_g4_constant_buffer;
#endif
typedef enum {
KINC_G4_TEXTURE_ADDRESSING_REPEAT,
KINC_G4_TEXTURE_ADDRESSING_MIRROR,
KINC_G4_TEXTURE_ADDRESSING_CLAMP,
KINC_G4_TEXTURE_ADDRESSING_BORDER
} kinc_g4_texture_addressing_t;
typedef enum { KINC_G4_TEXTURE_DIRECTION_U, KINC_G4_TEXTURE_DIRECTION_V, KINC_G4_TEXTURE_DIRECTION_W } kinc_g4_texture_direction_t;
typedef enum { KINC_G4_TEXTURE_FILTER_POINT, KINC_G4_TEXTURE_FILTER_LINEAR, KINC_G4_TEXTURE_FILTER_ANISOTROPIC } kinc_g4_texture_filter_t;
typedef enum {
KINC_G4_MIPMAP_FILTER_NONE,
KINC_G4_MIPMAP_FILTER_POINT,
KINC_G4_MIPMAP_FILTER_LINEAR // linear texture filter + linear mip filter -> trilinear filter
} kinc_g4_mipmap_filter_t;
/// <summary>
/// Returns whether instanced rendering (kinc_g4_draw_indexed_vertices_instanced and pals) is supported.
/// </summary>
/// <returns>Whether instanced rendering is supported</returns>
KINC_FUNC bool kinc_g4_supports_instanced_rendering(void);
/// <summary>
/// Returns whether GPU-compute (the functions in kinc/graphics4/compute.h) is supported.
/// </summary>
/// <returns>Whether GPU-compute is supported</returns>
KINC_FUNC bool kinc_g4_supports_compute_shaders(void);
/// <summary>
/// Returns whether blend-constants (see kinc_g4_set_blend_constant and the blending-properties for pipelines) are supported.
/// </summary>
/// <returns>Whether blend-constants are supported</returns>
KINC_FUNC bool kinc_g4_supports_blend_constants(void);
/// <summary>
/// Returns whether textures are supported which have widths/heights which are not powers of two.
/// </summary>
/// <returns>Whether non power of two texture-sizes are supported</returns>
KINC_FUNC bool kinc_g4_supports_non_pow2_textures(void);
/// <summary>
/// Returns whether render-targets are upside down. This happens in OpenGL and there is currently no automatic mitigation.
/// </summary>
/// <returns>Whether render-targets are upside down</returns>
KINC_FUNC bool kinc_g4_render_targets_inverted_y(void);
/// <summary>
/// Returns how many textures can be used at the same time in a fragment-shader.
/// </summary>
/// <returns>The number of textures</returns>
KINC_FUNC int kinc_g4_max_bound_textures(void);
/// <summary>
/// Kicks off lingering work - may or may not actually do anything depending on the underlying graphics-API.
/// </summary>
KINC_FUNC void kinc_g4_flush(void);
/// <summary>
/// Needs to be called before rendering to a window. Typically called at the start of each frame.
/// </summary>
/// <param name="window">The window to render to</param>
KINC_FUNC void kinc_g4_begin(int window);
/// <summary>
/// Needs to be called after rendering to a window. Typically called at the end of each frame.
/// </summary>
/// <param name="window">The window to render to</param>
/// <returns></returns>
KINC_FUNC void kinc_g4_end(int window);
/// <summary>
/// Needs to be called to make the rendered frame visible. Typically called at the very end of each frame.
/// </summary>
KINC_FUNC bool kinc_g4_swap_buffers(void);
#define KINC_G4_CLEAR_COLOR 1
#define KINC_G4_CLEAR_DEPTH 2
#define KINC_G4_CLEAR_STENCIL 4
/// <summary>
/// Clears the color, depth and/or stencil-components of the current framebuffer or render-target.
/// </summary>
/// <param name="flags">Defines what components to clear</param>
/// <param name="color">The color-value to clear to</param>
/// <param name="depth">The depth-value to clear to</param>
/// <param name="stencil">The stencil-value to clear to</param>
KINC_FUNC void kinc_g4_clear(unsigned flags, unsigned color, float depth, int stencil);
/// <summary>
/// Sets the viewport which defines the portion of the framebuffer or render-target things are rendered into. By default the viewport is equivalent to the full
/// size of the current render-target or framebuffer.
/// </summary>
/// <param name="x">The x-offset of the viewport from the left of the screen in pixels</param>
/// <param name="y">The y-offset of the viewport from the top of the screen in pixels</param>
/// <param name="width">The width of the viewport in pixels</param>
/// <param name="height">The height of the viewport in pixels</param>
KINC_FUNC void kinc_g4_viewport(int x, int y, int width, int height);
/// <summary>
/// Enables and defines the scissor-rect. When the scissor-rect is enabled, anything that's rendered outside of the scissor-rect will be ignored.
/// </summary>
/// <param name="x">The x-offset of the scissor-rect from the left of the screen in pixels</param>
/// <param name="y">The y-offset of the scissor-rect from the top of the screen in pixels</param>
/// <param name="width">The width of the scissor-rect in pixels</param>
/// <param name="height">The height of the scissor-rect in pixels</param>
KINC_FUNC void kinc_g4_scissor(int x, int y, int width, int height);
/// <summary>
/// Disables the scissor-rect.
/// </summary>
KINC_FUNC void kinc_g4_disable_scissor(void);
/// <summary>
/// Draws the entire content of the currently set index-buffer and vertex-buffer. G4 can only draw triangle-lists using vertex-indices as this is what GPUs tend
/// to be optimized for.
/// </summary>
KINC_FUNC void kinc_g4_draw_indexed_vertices(void);
/// <summary>
/// Draws a part of the content of the currently set index-buffer and vertex-buffer. G4 can only draw triangle-lists using vertex-indices as this is what GPUs
/// tend to be optimized for.
/// </summary>
/// <param name="start">The offset into the index-buffer</param>
/// <param name="count">The number of indices to use</param>
KINC_FUNC void kinc_g4_draw_indexed_vertices_from_to(int start, int count);
/// <summary>
/// Draws a part of the content of the currently set index-buffer and vertex-buffer and additionally applies a general offset into the vertex-buffer. G4 can
/// only draw triangle-lists using vertex-indices as this is what GPUs tend to be optimized for.
/// </summary>
/// <param name="start">The offset into the index-buffer</param>
/// <param name="count">The number of indices to use</param>
/// <param name="vertex_offset">The offset into the vertex-buffer which is added to each index read from the index-buffer</param>
KINC_FUNC void kinc_g4_draw_indexed_vertices_from_to_from(int start, int count, int vertex_offset);
KINC_FUNC void kinc_g4_draw_indexed_vertices_instanced(int instanceCount);
KINC_FUNC void kinc_g4_draw_indexed_vertices_instanced_from_to(int instanceCount, int start, int count);
KINC_FUNC void kinc_g4_set_texture_addressing(kinc_g4_texture_unit_t unit, kinc_g4_texture_direction_t dir, kinc_g4_texture_addressing_t addressing);
KINC_FUNC void kinc_g4_set_texture3d_addressing(kinc_g4_texture_unit_t unit, kinc_g4_texture_direction_t dir, kinc_g4_texture_addressing_t addressing);
/// <summary>
/// Sets the pipeline for the next draw-call. The pipeline defines most rendering-state including the shaders to be used.
/// </summary>
/// <param name="pipeline">The pipeline to set</param>
KINC_FUNC void kinc_g4_set_pipeline(struct kinc_g4_pipeline *pipeline);
KINC_FUNC void kinc_g4_set_stencil_reference_value(int value);
/// <summary>
/// Sets the blend constant used for `KINC_G4_BLEND_CONSTANT` or `KINC_G4_INV_BLEND_CONSTANT`
/// </summary>
KINC_FUNC void kinc_g4_set_blend_constant(float r, float g, float b, float a);
#ifdef KINC_KONG
KINC_FUNC void kinc_g4_set_constant_buffer(uint32_t id, struct kinc_g4_constant_buffer *buffer);
#endif
/// <summary>
/// Assigns an integer to a constant/uniform in the currently set pipeline.
/// </summary>
/// <param name="location">The location of the constant/uniform to assign the value to</param>
/// <param name="value">The value to assign to the constant/uniform</param>
KINC_FUNC void kinc_g4_set_int(kinc_g4_constant_location_t location, int value);
/// <summary>
/// Assigns two integers to a constant/uniform in the currently set pipeline.
/// </summary>
/// <param name="location">The location of the constant/uniform to assign the values to</param>
/// <param name="value1">The value to assign to the first component of the constant/uniform</param>
/// <param name="value2">The value to assign to the second component of the constant/uniform</param>
KINC_FUNC void kinc_g4_set_int2(kinc_g4_constant_location_t location, int value1, int value2);
/// <summary>
/// Assigns three integers to a constant/uniform in the currently set pipeline.
/// </summary>
/// <param name="location">The location of the constant/uniform to assign the values to</param>
/// <param name="value1">The value to assign to the first component of the constant/uniform</param>
/// <param name="value2">The value to assign to the second component of the constant/uniform</param>
/// <param name="value3">The value to assign to the third component of the constant/uniform</param>
KINC_FUNC void kinc_g4_set_int3(kinc_g4_constant_location_t location, int value1, int value2, int value3);
/// <summary>
/// Assigns four integers to a constant/uniform in the currently set pipeline.
/// </summary>
/// <param name="location">The location of the constant/uniform to assign the values to</param>
/// <param name="value1">The value to assign to the first component of the constant/uniform</param>
/// <param name="value2">The value to assign to the second component of the constant/uniform</param>
/// <param name="value3">The value to assign to the third component of the constant/uniform</param>
/// <param name="value4">The value to assign to the fourth component of the constant/uniform</param>
KINC_FUNC void kinc_g4_set_int4(kinc_g4_constant_location_t location, int value1, int value2, int value3, int value4);
/// <summary>
/// Assigns a bunch of integers to a constant/uniform in the currently set pipeline.
/// </summary>
/// <param name="location">The location of the constant/uniform to assign the values to</param>
/// <param name="value">The values to assign to the constant/uniform</param>
/// <param name="value">The number of values to assign to the constant/uniform</param>
KINC_FUNC void kinc_g4_set_ints(kinc_g4_constant_location_t location, int *values, int count);
/// <summary>
/// Assigns a float to a constant/uniform in the currently set pipeline.
/// </summary>
/// <param name="location">The location of the constant/uniform to assign the value to</param>
/// <param name="value">The value to assign to the constant/uniform</param>
KINC_FUNC void kinc_g4_set_float(kinc_g4_constant_location_t location, float value);
/// <summary>
/// Assigns two floats to a constant/uniform in the currently set pipeline.
/// </summary>
/// <param name="location">The location of the constant/uniform to assign the values to</param>
/// <param name="value1">The value to assign to the first constant/uniform</param>
/// <param name="value2">The value to assign to the second constant/uniform</param>
KINC_FUNC void kinc_g4_set_float2(kinc_g4_constant_location_t location, float value1, float value2);
/// <summary>
/// Assigns three floats to a constant/uniform in the currently set pipeline.
/// </summary>
/// <param name="location">The location of the constant/uniform to assign the values to</param>
/// <param name="value1">The value to assign to the first constant/uniform</param>
/// <param name="value2">The value to assign to the second constant/uniform</param>
/// <param name="value3">The value to assign to the third constant/uniform</param>
KINC_FUNC void kinc_g4_set_float3(kinc_g4_constant_location_t location, float value1, float value2, float value3);
/// <summary>
/// Assigns four floats to a constant/uniform in the currently set pipeline.
/// </summary>
/// <param name="location">The location of the constant/uniform to assign the values to</param>
/// <param name="value1">The value to assign to the first constant/uniform</param>
/// <param name="value2">The value to assign to the second constant/uniform</param>
/// <param name="value3">The value to assign to the third constant/uniform</param>
/// <param name="value4">The value to assign to the fourth constant/uniform</param>
KINC_FUNC void kinc_g4_set_float4(kinc_g4_constant_location_t location, float value1, float value2, float value3, float value4);
/// <summary>
/// Assigns a bunch of floats to a constant/uniform in the currently set pipeline.
/// </summary>
/// <param name="location">The location of the constant/uniform to assign the values to</param>
/// <param name="value">The values to assign to the constant/uniform</param>
/// <param name="value">The number of values to assign to the constant/uniform</param>
KINC_FUNC void kinc_g4_set_floats(kinc_g4_constant_location_t location, float *values, int count);
/// <summary>
/// Assigns a bool to a constant/uniform in the currently set pipeline.
/// </summary>
/// <param name="location">The location of the constant/uniform to assign the value to</param>
/// <param name="value">The value to assign to the constant/uniform</param>
KINC_FUNC void kinc_g4_set_bool(kinc_g4_constant_location_t location, bool value);
/// <summary>
/// Assigns a 3x3-matrix to a constant/uniform in the currently set pipeline.
/// </summary>
/// <param name="location">The location of the constant/uniform to assign the value to</param>
/// <param name="value">The value to assign to the constant/uniform</param>
KINC_FUNC void kinc_g4_set_matrix3(kinc_g4_constant_location_t location, kinc_matrix3x3_t *value);
/// <summary>
/// Assigns a 4x4-matrix to a constant/uniform in the currently set pipeline.
/// </summary>
/// <param name="location">The location of the constant/uniform to assign the value to</param>
/// <param name="value">The value to assign to the constant/uniform</param>
KINC_FUNC void kinc_g4_set_matrix4(kinc_g4_constant_location_t location, kinc_matrix4x4_t *value);
/// <summary>
/// Set the texture-sampling-mode for upscaled textures.
/// </summary>
/// <param name="unit">The texture-unit to set the texture-sampling-mode for</param>
/// <param name="filter">The mode to set</param>
KINC_FUNC void kinc_g4_set_texture_magnification_filter(kinc_g4_texture_unit_t unit, kinc_g4_texture_filter_t filter);
/// <summary>
/// Set the texture-sampling-mode for upscaled 3D-textures.
/// </summary>
/// <param name="unit">The texture-unit to set the texture-sampling-mode for</param>
/// <param name="filter">The mode to set</param>
KINC_FUNC void kinc_g4_set_texture3d_magnification_filter(kinc_g4_texture_unit_t texunit, kinc_g4_texture_filter_t filter);
/// <summary>
/// Set the texture-sampling-mode for downscaled textures.
/// </summary>
/// <param name="unit">The texture-unit to set the texture-sampling-mode for</param>
/// <param name="filter">The mode to set</param>
KINC_FUNC void kinc_g4_set_texture_minification_filter(kinc_g4_texture_unit_t unit, kinc_g4_texture_filter_t filter);
/// <summary>
/// Set the texture-sampling-mode for downscaled 3D-textures.
/// </summary>
/// <param name="unit">The texture-unit to set the texture-sampling-mode for</param>
/// <param name="filter">The mode to set</param>
KINC_FUNC void kinc_g4_set_texture3d_minification_filter(kinc_g4_texture_unit_t texunit, kinc_g4_texture_filter_t filter);
/// <summary>
/// Sets the mipmap-sampling-mode which defines whether mipmaps are used at all and if so whether the two neighbouring mipmaps are linearly interpolated.
/// </summary>
/// <param name="unit">The texture-unit to set the mipmap-sampling-mode for</param>
/// <param name="filter">The mode to set</param>
KINC_FUNC void kinc_g4_set_texture_mipmap_filter(kinc_g4_texture_unit_t unit, kinc_g4_mipmap_filter_t filter);
/// <summary>
/// Sets the mipmap-sampling-mode for a 3D-texture which defines whether mipmaps are used at all and if so whether the two neighbouring mipmaps are linearly
/// interpolated.
/// </summary>
/// <param name="unit">The texture-unit to set the mipmap-sampling-mode for</param>
/// <param name="filter">The mode to set</param>
KINC_FUNC void kinc_g4_set_texture3d_mipmap_filter(kinc_g4_texture_unit_t texunit, kinc_g4_mipmap_filter_t filter);
KINC_FUNC void kinc_g4_set_texture_compare_mode(kinc_g4_texture_unit_t unit, bool enabled);
KINC_FUNC void kinc_g4_set_texture_compare_func(kinc_g4_texture_unit_t unit, kinc_g4_compare_mode_t mode);
KINC_FUNC void kinc_g4_set_cubemap_compare_mode(kinc_g4_texture_unit_t unit, bool enabled);
KINC_FUNC void kinc_g4_set_cubemap_compare_func(kinc_g4_texture_unit_t unit, kinc_g4_compare_mode_t mode);
KINC_FUNC void kinc_g4_set_texture_max_anisotropy(kinc_g4_texture_unit_t unit, uint16_t max_anisotropy);
KINC_FUNC void kinc_g4_set_cubemap_max_anisotropy(kinc_g4_texture_unit_t unit, uint16_t max_anisotropy);
KINC_FUNC void kinc_g4_set_texture_lod(kinc_g4_texture_unit_t unit, float lod_min_clamp, float lod_max_clamp);
KINC_FUNC void kinc_g4_set_cubemap_lod(kinc_g4_texture_unit_t unit, float lod_min_clamp, float lod_max_clamp);
/// <summary>
/// Sets the framebuffer (aka the actual contents of the current window) to be the target of any future draw-calls.
/// </summary>
KINC_FUNC void kinc_g4_restore_render_target(void);
/// <summary>
/// Sets the passed render-targets to be the target of any future draw-calls.
/// </summary>
/// <param name="targets">An array of render-targets</param>
/// <param name="count">The number of render-targets in the render-target-array</param>
KINC_FUNC void kinc_g4_set_render_targets(struct kinc_g4_render_target **targets, int count);
KINC_FUNC void kinc_g4_set_render_target_face(struct kinc_g4_render_target *texture, int face);
#ifdef KINC_KONG
/// <summary>
/// Assigns a texture to a texture-unit for sampled access via GLSL's texture.
/// </summary>
/// <param name="unit">The unit to assign this texture to</param>
/// <param name="texture">The texture to assign to the unit</param>
KINC_FUNC void kinc_g4_set_texture(uint32_t unit, struct kinc_g4_texture *texture);
#else
/// <summary>
/// Assigns a texture to a texture-unit for sampled access via GLSL's texture.
/// </summary>
/// <param name="unit">The unit to assign this texture to</param>
/// <param name="texture">The texture to assign to the unit</param>
KINC_FUNC void kinc_g4_set_texture(kinc_g4_texture_unit_t unit, struct kinc_g4_texture *texture);
#endif
/// <summary>
/// Assigns a texture to a texture-unit for direct access via GLSL's texelFetch (as
/// opposed to GLSL's texture). The name of this functions is unfortunately based
/// on OpenGL's confusing terminology.
/// </summary>
/// <param name="unit">The unit to assign this texture to</param>
/// <param name="texture">The texture to assign to the unit</param>
KINC_FUNC void kinc_g4_set_image_texture(kinc_g4_texture_unit_t unit, struct kinc_g4_texture *texture);
KINC_FUNC bool kinc_g4_init_occlusion_query(unsigned *occlusionQuery);
KINC_FUNC void kinc_g4_delete_occlusion_query(unsigned occlusionQuery);
KINC_FUNC void kinc_g4_start_occlusion_query(unsigned occlusionQuery);
KINC_FUNC void kinc_g4_end_occlusion_query(unsigned occlusionQuery);
KINC_FUNC bool kinc_g4_are_query_results_available(unsigned occlusionQuery);
KINC_FUNC void kinc_g4_get_query_results(unsigned occlusionQuery, unsigned *pixelCount);
/// <summary>
/// Assigns a texture-array to a texture-unit.
/// </summary>
/// <param name="unit">The unit to assign the texture-array to</param>
/// <param name="array">The texture-array to assign to the texture-unit</param>
KINC_FUNC void kinc_g4_set_texture_array(kinc_g4_texture_unit_t unit, struct kinc_g4_texture_array *array);
/// <summary>
/// Returns the currently used number of samples for hardware-antialiasing.
/// </summary>
/// <returns>The number of samples</returns>
KINC_FUNC int kinc_g4_antialiasing_samples(void);
/// <summary>
/// Sets the number of samples used for hardware-antialiasing. This typically uses multisampling and typically only works with a few specific numbers of
/// sample-counts - 2 and 4 are pretty safe bets. It also might do nothing at all.
/// </summary>
/// <param name="samples">The number of samples</param>
KINC_FUNC void kinc_g4_set_antialiasing_samples(int samples);
#ifndef KINC_DOCS
void kinc_g4_internal_init(void);
void kinc_g4_internal_init_window(int window, int depth_buffer_bits, int stencil_buffer_bits, bool vsync);
void kinc_g4_internal_destroy_window(int window);
void kinc_g4_internal_destroy(void);
#endif
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,84 @@
#pragma once
#include <kinc/global.h>
#include "usage.h"
#include <kinc/backend/graphics4/indexbuffer.h>
/*! \file indexbuffer.h
\brief Provides functions for setting up and using index-buffers.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef enum kinc_g4_index_buffer_format { KINC_G4_INDEX_BUFFER_FORMAT_32BIT, KINC_G4_INDEX_BUFFER_FORMAT_16BIT } kinc_g4_index_buffer_format_t;
typedef struct kinc_g4_index_buffer {
kinc_g4_index_buffer_impl_t impl;
} kinc_g4_index_buffer_t;
/// <summary>
/// Initializes an index-buffer.
/// </summary>
/// <param name="buffer">The buffer to initialize</param>
/// <param name="count">The number of indices to allocate for the buffer</param>
/// <param name="format">The integer-format of the buffer</param>
/// <param name="usage">A hint for how the buffer will be used</param>
KINC_FUNC void kinc_g4_index_buffer_init(kinc_g4_index_buffer_t *buffer, int count, kinc_g4_index_buffer_format_t format, kinc_g4_usage_t usage);
/// <summary>
/// Destroys an index-buffer.
/// </summary>
/// <param name="buffer">The buffer to destroy</param>
KINC_FUNC void kinc_g4_index_buffer_destroy(kinc_g4_index_buffer_t *buffer);
/// <summary>
/// Locks an index-buffer so its contents can be modified.
/// </summary>
/// <param name="buffer">The buffer to lock</param>
/// <returns>The contents of the index-buffer in uint32s or uint16s depending on the format provided when initializing</returns>
KINC_FUNC void *kinc_g4_index_buffer_lock_all(kinc_g4_index_buffer_t *buffer);
/// <summary>
/// Locks part of a vertex-buffer to modify its contents.
/// </summary>
/// <param name="buffer">The buffer to lock</param>
/// <param name="start">The index of the first index to lock</param>
/// <param name="count">The number of indices to lock</param>
/// <returns>The contents of the index-buffer, starting at start, in uint32s or uint16s depending on the format provided when initializing</returns>
KINC_FUNC void *kinc_g4_index_buffer_lock(kinc_g4_index_buffer_t *buffer, int start, int count);
/// <summary>
/// Unlocks an index-buffer after locking it so the changed buffer-contents can be used.
/// </summary>
/// <param name="buffer">The buffer to unlock</param>
KINC_FUNC void kinc_g4_index_buffer_unlock_all(kinc_g4_index_buffer_t *buffer);
/// <summary>
/// Unlocks part of an index-buffer after locking so the changed buffer-contents can be used.
/// </summary>
/// <param name="buffer">The buffer to unlock</param>
/// <param name="count">The number of indices to unlock, starting from the start-index from the previous lock-call</param>
KINC_FUNC void kinc_g4_index_buffer_unlock(kinc_g4_index_buffer_t *buffer, int count);
/// <summary>
/// Returns the number of indices in the buffer.
/// </summary>
/// <param name="buffer">The buffer to query for its number of indices</param>
/// <returns>The number of indices</returns>
KINC_FUNC int kinc_g4_index_buffer_count(kinc_g4_index_buffer_t *buffer);
void kinc_internal_g4_index_buffer_set(kinc_g4_index_buffer_t *buffer);
/// <summary>
/// Sets an index-buffer to be used for the next draw-command.
/// </summary>
/// <param name="buffer">The buffer to use</param>
KINC_FUNC void kinc_g4_set_index_buffer(kinc_g4_index_buffer_t *buffer);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,53 @@
#include "pipeline.h"
void kinc_g4_internal_pipeline_set_defaults(kinc_g4_pipeline_t *state) {
for (int i = 0; i < 16; ++i)
state->input_layout[i] = NULL;
state->vertex_shader = NULL;
state->fragment_shader = NULL;
state->geometry_shader = NULL;
state->tessellation_control_shader = NULL;
state->tessellation_evaluation_shader = NULL;
state->cull_mode = KINC_G4_CULL_NOTHING;
state->depth_write = false;
state->depth_mode = KINC_G4_COMPARE_ALWAYS;
state->stencil_front_mode = KINC_G4_COMPARE_ALWAYS;
state->stencil_front_both_pass = KINC_G4_STENCIL_KEEP;
state->stencil_front_depth_fail = KINC_G4_STENCIL_KEEP;
state->stencil_front_fail = KINC_G4_STENCIL_KEEP;
state->stencil_back_mode = KINC_G4_COMPARE_ALWAYS;
state->stencil_back_both_pass = KINC_G4_STENCIL_KEEP;
state->stencil_back_depth_fail = KINC_G4_STENCIL_KEEP;
state->stencil_back_fail = KINC_G4_STENCIL_KEEP;
state->stencil_reference_value = 0;
state->stencil_read_mask = 0xff;
state->stencil_write_mask = 0xff;
state->blend_source = KINC_G4_BLEND_ONE;
state->blend_destination = KINC_G4_BLEND_ZERO;
state->blend_operation = KINC_G4_BLENDOP_ADD;
state->alpha_blend_source = KINC_G4_BLEND_ONE;
state->alpha_blend_destination = KINC_G4_BLEND_ZERO;
state->alpha_blend_operation = KINC_G4_BLENDOP_ADD;
for (int i = 0; i < 8; ++i)
state->color_write_mask_red[i] = true;
for (int i = 0; i < 8; ++i)
state->color_write_mask_green[i] = true;
for (int i = 0; i < 8; ++i)
state->color_write_mask_blue[i] = true;
for (int i = 0; i < 8; ++i)
state->color_write_mask_alpha[i] = true;
state->color_attachment_count = 1;
for (int i = 0; i < 8; ++i)
state->color_attachment[i] = KINC_G4_RENDER_TARGET_FORMAT_32BIT;
state->depth_attachment_bits = 0;
state->stencil_attachment_bits = 0;
state->conservative_rasterization = false;
}

View File

@ -0,0 +1,161 @@
#pragma once
#include <kinc/global.h>
#include <kinc/graphics4/constantlocation.h>
#include <kinc/graphics4/rendertarget.h>
#include <kinc/graphics4/textureunit.h>
#include <kinc/backend/graphics4/pipeline.h>
/*! \file pipeline.h
\brief Provides functions for creating and using pipelines which configure the GPU for rendering.
*/
#ifdef __cplusplus
extern "C" {
#endif
struct kinc_g4_vertex_structure;
struct kinc_g4_shader;
typedef enum {
KINC_G4_BLEND_ONE,
KINC_G4_BLEND_ZERO,
KINC_G4_BLEND_SOURCE_ALPHA,
KINC_G4_BLEND_DEST_ALPHA,
KINC_G4_BLEND_INV_SOURCE_ALPHA,
KINC_G4_BLEND_INV_DEST_ALPHA,
KINC_G4_BLEND_SOURCE_COLOR,
KINC_G4_BLEND_DEST_COLOR,
KINC_G4_BLEND_INV_SOURCE_COLOR,
KINC_G4_BLEND_INV_DEST_COLOR,
KINC_G4_BLEND_CONSTANT,
KINC_G4_BLEND_INV_CONSTANT
} kinc_g4_blending_factor_t;
typedef enum {
KINC_G4_BLENDOP_ADD,
KINC_G4_BLENDOP_SUBTRACT,
KINC_G4_BLENDOP_REVERSE_SUBTRACT,
KINC_G4_BLENDOP_MIN,
KINC_G4_BLENDOP_MAX
} kinc_g4_blending_operation_t;
typedef enum {
KINC_G4_COMPARE_ALWAYS,
KINC_G4_COMPARE_NEVER,
KINC_G4_COMPARE_EQUAL,
KINC_G4_COMPARE_NOT_EQUAL,
KINC_G4_COMPARE_LESS,
KINC_G4_COMPARE_LESS_EQUAL,
KINC_G4_COMPARE_GREATER,
KINC_G4_COMPARE_GREATER_EQUAL
} kinc_g4_compare_mode_t;
typedef enum { KINC_G4_CULL_CLOCKWISE, KINC_G4_CULL_COUNTER_CLOCKWISE, KINC_G4_CULL_NOTHING } kinc_g4_cull_mode_t;
typedef enum {
KINC_G4_STENCIL_KEEP,
KINC_G4_STENCIL_ZERO,
KINC_G4_STENCIL_REPLACE,
KINC_G4_STENCIL_INCREMENT,
KINC_G4_STENCIL_INCREMENT_WRAP,
KINC_G4_STENCIL_DECREMENT,
KINC_G4_STENCIL_DECREMENT_WRAP,
KINC_G4_STENCIL_INVERT
} kinc_g4_stencil_action_t;
typedef struct kinc_g4_pipeline {
struct kinc_g4_vertex_structure *input_layout[16];
struct kinc_g4_shader *vertex_shader;
struct kinc_g4_shader *fragment_shader;
struct kinc_g4_shader *geometry_shader;
struct kinc_g4_shader *tessellation_control_shader;
struct kinc_g4_shader *tessellation_evaluation_shader;
kinc_g4_cull_mode_t cull_mode;
bool depth_write;
kinc_g4_compare_mode_t depth_mode;
kinc_g4_compare_mode_t stencil_front_mode;
kinc_g4_stencil_action_t stencil_front_both_pass;
kinc_g4_stencil_action_t stencil_front_depth_fail;
kinc_g4_stencil_action_t stencil_front_fail;
kinc_g4_compare_mode_t stencil_back_mode;
kinc_g4_stencil_action_t stencil_back_both_pass;
kinc_g4_stencil_action_t stencil_back_depth_fail;
kinc_g4_stencil_action_t stencil_back_fail;
int stencil_reference_value;
int stencil_read_mask;
int stencil_write_mask;
// One, Zero deactivates blending
kinc_g4_blending_factor_t blend_source;
kinc_g4_blending_factor_t blend_destination;
kinc_g4_blending_operation_t blend_operation;
kinc_g4_blending_factor_t alpha_blend_source;
kinc_g4_blending_factor_t alpha_blend_destination;
kinc_g4_blending_operation_t alpha_blend_operation;
bool color_write_mask_red[8]; // Per render target
bool color_write_mask_green[8];
bool color_write_mask_blue[8];
bool color_write_mask_alpha[8];
int color_attachment_count;
kinc_g4_render_target_format_t color_attachment[8];
int depth_attachment_bits;
int stencil_attachment_bits;
bool conservative_rasterization;
kinc_g4_pipeline_impl_t impl;
} kinc_g4_pipeline_t;
/// <summary>
/// Initializes a pipeline.
/// </summary>
/// <param name="state">The pipeline to initialize</param>
KINC_FUNC void kinc_g4_pipeline_init(kinc_g4_pipeline_t *pipeline);
/// <summary>
/// Destroys a pipeline.
/// </summary>
/// <param name="pipeline">The pipeline to destroy</param>
KINC_FUNC void kinc_g4_pipeline_destroy(kinc_g4_pipeline_t *pipeline);
/// <summary>
/// Compiles a pipeline. After a pipeline has been compiled it is finalized. It cannot be compiled again and further changes to the pipeline are ignored.
/// </summary>
/// <param name="pipeline">The pipeline to compile</param>
KINC_FUNC void kinc_g4_pipeline_compile(kinc_g4_pipeline_t *pipeline);
#ifndef KINC_KONG
/// <summary>
/// Searches for a constant/uniform and returns a constant-location which can be used to change the constant/uniform.
/// </summary>
/// <param name="pipeline">The pipeline to search in</param>
/// <param name="name">The name of the constant/uniform to find</param>
/// <returns>The constant-location of the constant/uniform</returns>
KINC_FUNC kinc_g4_constant_location_t kinc_g4_pipeline_get_constant_location(kinc_g4_pipeline_t *pipeline, const char *name);
/// <summary>
/// Searches for a texture-declaration and returns a texture-unit which can be used to assign a texture.
/// </summary>
/// <param name="pipeline">The pipeline to search in</param>
/// <param name="name">The name of the texture-declaration to search for</param>
/// <returns>The texture-unit of the texture-declaration</returns>
KINC_FUNC kinc_g4_texture_unit_t kinc_g4_pipeline_get_texture_unit(kinc_g4_pipeline_t *pipeline, const char *name);
#endif
void kinc_g4_internal_set_pipeline(kinc_g4_pipeline_t *pipeline);
void kinc_g4_internal_pipeline_set_defaults(kinc_g4_pipeline_t *pipeline);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,11 @@
#include "rendertarget.h"
void kinc_g4_render_target_init(kinc_g4_render_target_t *renderTarget, int width, int height, kinc_g4_render_target_format_t format, int depthBufferBits,
int stencilBufferBits) {
kinc_g4_render_target_init_with_multisampling(renderTarget, width, height, format, depthBufferBits, stencilBufferBits, 1);
}
void kinc_g4_render_target_init_cube(kinc_g4_render_target_t *renderTarget, int cubeMapSize, kinc_g4_render_target_format_t format, int depthBufferBits,
int stencilBufferBits) {
kinc_g4_render_target_init_cube_with_multisampling(renderTarget, cubeMapSize, format, depthBufferBits, stencilBufferBits, 1);
}

View File

@ -0,0 +1,142 @@
#pragma once
#include <kinc/global.h>
#include <kinc/backend/graphics4/rendertarget.h>
#include "textureunit.h"
#include <stdint.h>
/*! \file rendertarget.h
\brief Provides functions for handling render-targets.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef enum kinc_g4_render_target_format {
KINC_G4_RENDER_TARGET_FORMAT_32BIT,
KINC_G4_RENDER_TARGET_FORMAT_64BIT_FLOAT,
KINC_G4_RENDER_TARGET_FORMAT_32BIT_RED_FLOAT,
KINC_G4_RENDER_TARGET_FORMAT_128BIT_FLOAT,
KINC_G4_RENDER_TARGET_FORMAT_16BIT_DEPTH,
KINC_G4_RENDER_TARGET_FORMAT_8BIT_RED,
KINC_G4_RENDER_TARGET_FORMAT_16BIT_RED_FLOAT
} kinc_g4_render_target_format_t;
typedef struct kinc_g4_render_target {
int width;
int height;
int texWidth;
int texHeight;
bool isCubeMap;
bool isDepthAttachment;
kinc_g4_render_target_impl_t impl;
} kinc_g4_render_target_t;
/// <summary>
/// Allocates and initializes a regular render-target.
/// </summary>
/// <param name="renderTarget"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="format"></param>
/// <param name="depthBufferBits"></param>
/// <param name="stencilBufferBits"></param>
KINC_FUNC void kinc_g4_render_target_init(kinc_g4_render_target_t *renderTarget, int width, int height, kinc_g4_render_target_format_t format,
int depthBufferBits, int stencilBufferBits);
/// <summary>
/// Allocates and initializes a multi-sampled render-target if possible - otherwise it falls back to a regular render-target.
/// </summary>
/// <param name="renderTarget"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="format"></param>
/// <param name="depthBufferBits"></param>
/// <param name="stencilBufferBits"></param>
/// <param name="samples_per_pixel"></param>
KINC_FUNC void kinc_g4_render_target_init_with_multisampling(kinc_g4_render_target_t *renderTarget, int width, int height,
kinc_g4_render_target_format_t format, int depthBufferBits, int stencilBufferBits,
int samples_per_pixel);
/// <summary>
/// Allocates and initializes a render-target-cube-map.
/// </summary>
/// <param name="renderTarget"></param>
/// <param name="cubeMapSize"></param>
/// <param name="format"></param>
/// <param name="depthBufferBits"></param>
/// <param name="stencilBufferBits"></param>
KINC_FUNC void kinc_g4_render_target_init_cube(kinc_g4_render_target_t *renderTarget, int cubeMapSize, kinc_g4_render_target_format_t format,
int depthBufferBits, int stencilBufferBits);
/// <summary>
/// Allocates and initializes a multi-sampled render-target-cube-map. Can fall back to a non-multi-sampled cube-map.
/// </summary>
/// <param name="renderTarget"></param>
/// <param name="cubeMapSize"></param>
/// <param name="format"></param>
/// <param name="depthBufferBits"></param>
/// <param name="stencilBufferBits"></param>
/// <param name="samples_per_pixel"></param>
KINC_FUNC void kinc_g4_render_target_init_cube_with_multisampling(kinc_g4_render_target_t *renderTarget, int cubeMapSize, kinc_g4_render_target_format_t format,
int depthBufferBits, int stencilBufferBits, int samples_per_pixel);
/// <summary>
/// Deallocates and destroys a render-target.
/// </summary>
/// <param name="renderTarget">The render-target to destroy</param>
KINC_FUNC void kinc_g4_render_target_destroy(kinc_g4_render_target_t *renderTarget);
#ifdef KINC_KONG
/// <summary>
/// Uses the color-component of a render-target as a texture.
/// </summary>
/// <param name="renderTarget">The render-target to use</param>
/// <param name="unit">The texture-unit to assign the render-target to</param>
KINC_FUNC void kinc_g4_render_target_use_color_as_texture(kinc_g4_render_target_t *renderTarget, uint32_t unit);
#else
/// <summary>
/// Uses the color-component of a render-target as a texture.
/// </summary>
/// <param name="renderTarget">The render-target to use</param>
/// <param name="unit">The texture-unit to assign the render-target to</param>
KINC_FUNC void kinc_g4_render_target_use_color_as_texture(kinc_g4_render_target_t *renderTarget, kinc_g4_texture_unit_t unit);
#endif
/// <summary>
/// Uses the depth-component of a render-target as a texture.
/// </summary>
/// <param name="renderTarget">The render-target to use</param>
/// <param name="unit">The texture-unit to assign the render-target to</param>
KINC_FUNC void kinc_g4_render_target_use_depth_as_texture(kinc_g4_render_target_t *renderTarget, kinc_g4_texture_unit_t unit);
/// <summary>
/// Copies the depth and stencil-components of one render-target into another one.
/// </summary>
/// <param name="renderTarget">The render-target to copy the data into</param>
/// <param name="source">The render-target from which to copy the data</param>
/// <returns></returns>
KINC_FUNC void kinc_g4_render_target_set_depth_stencil_from(kinc_g4_render_target_t *renderTarget, kinc_g4_render_target_t *source);
/// <summary>
/// Copies out the color-data from a render-target. Beware, this is very slow.
/// </summary>
/// <param name="renderTarget">The render-target to copy the color-data from</param>
/// <param name="data">A pointer to where the data will be copied to</param>
KINC_FUNC void kinc_g4_render_target_get_pixels(kinc_g4_render_target_t *renderTarget, uint8_t *data);
/// <summary>
/// Generates the mipmap-chain for a render-target.
/// </summary>
/// <param name="renderTarget">The render-target to create the mipmaps for</param>
/// <param name="levels">The number of mipmap-levels to generate</param>
KINC_FUNC void kinc_g4_render_target_generate_mipmaps(kinc_g4_render_target_t *renderTarget, int levels);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,58 @@
#pragma once
#include <kinc/global.h>
#include <kinc/backend/graphics4/shader.h>
/*! \file shader.h
\brief Provides functions for creating and destroying shaders.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef enum kinc_g4_shader_type {
KINC_G4_SHADER_TYPE_FRAGMENT,
KINC_G4_SHADER_TYPE_VERTEX,
KINC_G4_SHADER_TYPE_COMPUTE,
KINC_G4_SHADER_TYPE_GEOMETRY,
KINC_G4_SHADER_TYPE_TESSELLATION_CONTROL,
KINC_G4_SHADER_TYPE_TESSELLATION_EVALUATION,
KINC_G4_SHADER_TYPE_COUNT
} kinc_g4_shader_type_t;
typedef struct kinc_g4_shader {
kinc_g4_shader_impl_t impl;
} kinc_g4_shader_t;
/// <summary>
/// Initializes a shader based on system-specific shader-data. The system-specific shader-data is usually created per system by the krafix-shader-compiler which
/// is automatically called by kincmake.
/// </summary>
/// <param name="shader">The shader to initialize</param>
/// <param name="data">The system-specific shader-data</param>
/// <param name="length">The length of the system-specific shader-data in bytes</param>
/// <param name="type">The type of the shader</param>
KINC_FUNC void kinc_g4_shader_init(kinc_g4_shader_t *shader, const void *data, size_t length, kinc_g4_shader_type_t type);
/// <summary>
/// Initializes a shader from GLSL-source-code. This only works on some platforms and only if KRAFIX_LIBRARY define has been set and the krafix-shader-compiler
/// was compiled in library-mode and linked into the application.
/// </summary>
/// <param name="shader">The shader to initialize</param>
/// <param name="source">The GLSL-shader-source-code</param>
/// <param name="type">The type of the shader</param>
/// <returns>The number of errors the compiler encountered - hopefully it's zero.</returns>
KINC_FUNC int kinc_g4_shader_init_from_source(kinc_g4_shader_t *shader, const char *source, kinc_g4_shader_type_t type);
/// <summary>
/// Destroys a shader.
/// </summary>
/// <param name="shader">The shader to destroy</param>
KINC_FUNC void kinc_g4_shader_destroy(kinc_g4_shader_t *shader);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,106 @@
#pragma once
#include <kinc/global.h>
#include <kinc/backend/graphics4/texture.h>
#include <kinc/image.h>
/*! \file texture.h
\brief Provides functions for handling textures.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef kinc_image_t kinc_g4_image_t;
typedef struct kinc_g4_texture {
int tex_width;
int tex_height;
int tex_depth;
kinc_image_format_t format;
kinc_g4_texture_impl_t impl;
} kinc_g4_texture_t;
/// <summary>
/// Allocates and initializes a texture without copying any data into it.
/// </summary>
/// <param name="texture">The texture to initialize</param>
/// <param name="width">The width of the texture to create</param>
/// <param name="height">The height of the texture to create</param>
/// <param name="format">The format of the texture to create</param>
KINC_FUNC void kinc_g4_texture_init(kinc_g4_texture_t *texture, int width, int height, kinc_image_format_t format);
/// <summary>
/// Allocates and initializes a 3d-texture without copying any data into it.
/// </summary>
/// <param name="texture">The texture to initialize</param>
/// <param name="width">The width of the texture to create</param>
/// <param name="height">The height of the texture to create</param>
/// <param name="depth">The depth of the texture to create</param>
/// <param name="format">The format of the texture to create</param>
KINC_FUNC void kinc_g4_texture_init3d(kinc_g4_texture_t *texture, int width, int height, int depth, kinc_image_format_t format);
/// <summary>
/// Allocates and initializes a texture and copies image-data into it.
/// </summary>
/// <param name="texture">The texture to initialize</param>
/// <param name="image">The image which's data is copied into the texture</param>
KINC_FUNC void kinc_g4_texture_init_from_image(kinc_g4_texture_t *texture, kinc_image_t *image);
/// <summary>
/// Allocates and initializes a texture and copies image-data into it.
/// </summary>
/// <param name="texture">The texture to initialize</param>
/// <param name="image">The image which's data is copied into the texture</param>
KINC_FUNC void kinc_g4_texture_init_from_image3d(kinc_g4_texture_t *texture, kinc_image_t *image);
/// <summary>
/// Deallocates and destroys a texture.
/// </summary>
/// <param name="texture">The texture to destroy</param>
KINC_FUNC void kinc_g4_texture_destroy(kinc_g4_texture_t *texture);
KINC_FUNC unsigned char *kinc_g4_texture_lock(kinc_g4_texture_t *texture);
KINC_FUNC void kinc_g4_texture_unlock(kinc_g4_texture_t *texture);
/// <summary>
/// Clears parts of a texture to a color.
/// </summary>
KINC_FUNC void kinc_g4_texture_clear(kinc_g4_texture_t *texture, int x, int y, int z, int width, int height, int depth, unsigned color);
/// <summary>
/// Generates the mipmap-chain for a texture.
/// </summary>
/// <param name="renderTarget">The render-target to create the mipmaps for</param>
/// <param name="levels">The number of mipmap-levels to generate</param>
KINC_FUNC void kinc_g4_texture_generate_mipmaps(kinc_g4_texture_t *texture, int levels);
/// <summary>
/// Sets the mipmap for one level of a texture.
/// </summary>
/// <param name="texture">The texture to set a mipmap-level for</param>
/// <param name="mipmap">The image-data for the mipmap-level to set</param>
/// <param name="level">The mipmap-level to set</param>
KINC_FUNC void kinc_g4_texture_set_mipmap(kinc_g4_texture_t *texture, kinc_image_t *mipmap, int level);
/// <summary>
/// Returns the stride of the first mipmap-layer of the texture in bytes.
/// </summary>
/// <param name="texture">The texture to figure out the stride for</param>
/// <returns>The stride of the first mipmap-layer in bytes</returns>
KINC_FUNC int kinc_g4_texture_stride(kinc_g4_texture_t *texture);
#ifdef KORE_ANDROID
KINC_FUNC void kinc_g4_texture_init_from_id(kinc_g4_texture_t *texture, unsigned texid);
#endif
#if defined(KORE_IOS) || defined(KORE_MACOS)
KINC_FUNC void kinc_g4_texture_upload(kinc_g4_texture_t *texture, uint8_t *data, int stride);
#endif
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,37 @@
#pragma once
#include <kinc/global.h>
#include "texture.h"
#include <kinc/backend/graphics4/texturearray.h>
/*! \file texturearray.h
\brief Provides functions for creating and destroying texture-arrays.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef struct kinc_g4_texture_array {
kinc_g4_texture_array_impl_t impl;
} kinc_g4_texture_array_t;
/// <summary>
/// Allocates and initializes a texture-array based on an array of images.
/// </summary>
/// <param name="array">The texture-array to initialize</param>
/// <param name="images">The images to assign to the texture-array</param>
/// <param name="count">The number of images</param>
KINC_FUNC void kinc_g4_texture_array_init(kinc_g4_texture_array_t *array, kinc_image_t *images, int count);
/// <summary>
/// Deallocates and destroys a texture-array
/// </summary>
/// <param name="array">The texture-array to destroy</param>
KINC_FUNC void kinc_g4_texture_array_destroy(kinc_g4_texture_array_t *array);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,21 @@
#pragma once
#include <kinc/global.h>
#include <kinc/graphics4/shader.h>
/*! \file textureunit.h
\brief Provides a texture-unit-struct which is used for setting textures in a shader.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef struct kinc_g4_texture_unit {
int stages[KINC_G4_SHADER_TYPE_COUNT];
} kinc_g4_texture_unit_t;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,15 @@
#pragma once
/*! \file usage.h
\brief Provides the usage enum.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef enum kinc_g4_usage { KINC_G4_USAGE_STATIC, KINC_G4_USAGE_DYNAMIC, KINC_G4_USAGE_READABLE } kinc_g4_usage_t;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,20 @@
#include "vertexbuffer.h"
static void init_vertex_element(kinc_g4_vertex_element_t *element, const char *name, kinc_g4_vertex_data_t data) {
element->name = name;
element->data = data;
}
void kinc_g4_vertex_structure_init(kinc_g4_vertex_structure_t *structure) {
structure->size = 0;
structure->instanced = false;
}
void kinc_g4_vertex_structure_add(kinc_g4_vertex_structure_t *structure, const char *name, kinc_g4_vertex_data_t data) {
init_vertex_element(&structure->elements[structure->size++], name, data);
}
void kinc_g4_set_vertex_buffer(kinc_g4_vertex_buffer_t *buffer) {
kinc_g4_vertex_buffer_t *buffers[1] = {buffer};
kinc_g4_set_vertex_buffers(buffers, 1);
}

View File

@ -0,0 +1,101 @@
#pragma once
#include <kinc/global.h>
#include "usage.h"
#include "vertexstructure.h"
#include <kinc/backend/graphics4/vertexbuffer.h>
#include <stdbool.h>
/*! \file vertexbuffer.h
\brief Provides functions for setting up and using vertex-buffers.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef struct kinc_g4_vertex_buffer {
kinc_g4_vertex_buffer_impl_t impl;
} kinc_g4_vertex_buffer_t;
/// <summary>
/// Allocate and initialize a vertex-buffer.
/// </summary>
/// <param name="buffer">The buffer to initialize</param>
/// <param name="count">The number of vertices in the buffer</param>
/// <param name="structure">The structure of the buffer</param>
/// <param name="usage">A hint for how the buffer will be used</param>
/// <param name="instance_data_step_rate">The step-rate for instanced-rendering - use 0 if instanced-rendering will not be used with this buffer</param>
KINC_FUNC void kinc_g4_vertex_buffer_init(kinc_g4_vertex_buffer_t *buffer, int count, kinc_g4_vertex_structure_t *structure, kinc_g4_usage_t usage,
int instance_data_step_rate);
/// <summary>
/// Destroys a vertex-buffer.
/// </summary>
/// <param name="buffer">The buffer to destroy</param>
KINC_FUNC void kinc_g4_vertex_buffer_destroy(kinc_g4_vertex_buffer_t *buffer);
/// <summary>
/// Locks all of a vertex-buffer to modify its contents.
/// </summary>
/// <param name="buffer">The buffer to lock</param>
/// <returns>The contents of the buffer</returns>
KINC_FUNC float *kinc_g4_vertex_buffer_lock_all(kinc_g4_vertex_buffer_t *buffer);
/// <summary>
/// Locks part of a vertex-buffer to modify its contents.
/// </summary>
/// <param name="buffer">The buffer to lock</param>
/// <param name="start">The index of the first vertex to lock</param>
/// <param name="count">The number of vertices to lock</param>
/// <returns>The contents of the buffer, starting at start</returns>
KINC_FUNC float *kinc_g4_vertex_buffer_lock(kinc_g4_vertex_buffer_t *buffer, int start, int count);
/// <summary>
/// Unlock all of a vertex-buffer so the changed contents can be used.
/// </summary>
/// <param name="buffer">The buffer to unlock</param>
KINC_FUNC void kinc_g4_vertex_buffer_unlock_all(kinc_g4_vertex_buffer_t *buffer);
/// <summary>
/// Unlocks part of a vertex-buffer so the changed contents can be used.
/// </summary>
/// <param name="buffer">The buffer to unlock</param>
/// <param name="count">The number of vertices to unlock, starting from the start-vertex from the previous lock-call</param>
KINC_FUNC void kinc_g4_vertex_buffer_unlock(kinc_g4_vertex_buffer_t *buffer, int count);
/// <summary>
/// Returns the number of vertices in a buffer.
/// </summary>
/// <param name="buffer">The buffer to figure out the number of vertices for</param>
/// <returns>The number of vertices</returns>
KINC_FUNC int kinc_g4_vertex_buffer_count(kinc_g4_vertex_buffer_t *buffer);
/// <summary>
/// Returns the stride aka the size of one vertex of the buffer in bytes.
/// </summary>
/// <param name="buffer">The buffer to figure out the stride for</param>
/// <returns>The stride of the buffer in bytes</returns>
KINC_FUNC int kinc_g4_vertex_buffer_stride(kinc_g4_vertex_buffer_t *buffer);
int kinc_internal_g4_vertex_buffer_set(kinc_g4_vertex_buffer_t *buffer, int offset);
/// <summary>
/// Sets vertex-buffers for the next draw-call.
/// </summary>
/// <param name="buffers">The buffers to set</param>
/// <param name="count">The number of buffers to set</param>
KINC_FUNC void kinc_g4_set_vertex_buffers(kinc_g4_vertex_buffer_t **buffers, int count);
/// <summary>
/// Sets a vertex-buffer for the next draw-call.
/// </summary>
/// <param name="buffer">The buffer to set</param>
KINC_FUNC void kinc_g4_set_vertex_buffer(kinc_g4_vertex_buffer_t *buffer);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,157 @@
#pragma once
#include <kinc/global.h>
#include <stdbool.h>
/*! \file vertexstructure.h
\brief Provides functions for setting up the structure of vertices in a vertex-buffer.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef enum kinc_g4_vertex_data {
KINC_G4_VERTEX_DATA_NONE = 0,
KINC_G4_VERTEX_DATA_F32_1X = 1,
KINC_G4_VERTEX_DATA_F32_2X = 2,
KINC_G4_VERTEX_DATA_F32_3X = 3,
KINC_G4_VERTEX_DATA_F32_4X = 4,
KINC_G4_VERTEX_DATA_F32_4X4 = 5,
KINC_G4_VERTEX_DATA_I8_1X = 6,
KINC_G4_VERTEX_DATA_U8_1X = 7,
KINC_G4_VERTEX_DATA_I8_1X_NORMALIZED = 8,
KINC_G4_VERTEX_DATA_U8_1X_NORMALIZED = 9,
KINC_G4_VERTEX_DATA_I8_2X = 10,
KINC_G4_VERTEX_DATA_U8_2X = 11,
KINC_G4_VERTEX_DATA_I8_2X_NORMALIZED = 12,
KINC_G4_VERTEX_DATA_U8_2X_NORMALIZED = 13,
KINC_G4_VERTEX_DATA_I8_4X = 14,
KINC_G4_VERTEX_DATA_U8_4X = 15,
KINC_G4_VERTEX_DATA_I8_4X_NORMALIZED = 16,
KINC_G4_VERTEX_DATA_U8_4X_NORMALIZED = 17,
KINC_G4_VERTEX_DATA_I16_1X = 18,
KINC_G4_VERTEX_DATA_U16_1X = 19,
KINC_G4_VERTEX_DATA_I16_1X_NORMALIZED = 20,
KINC_G4_VERTEX_DATA_U16_1X_NORMALIZED = 21,
KINC_G4_VERTEX_DATA_I16_2X = 22,
KINC_G4_VERTEX_DATA_U16_2X = 23,
KINC_G4_VERTEX_DATA_I16_2X_NORMALIZED = 24,
KINC_G4_VERTEX_DATA_U16_2X_NORMALIZED = 25,
KINC_G4_VERTEX_DATA_I16_4X = 26,
KINC_G4_VERTEX_DATA_U16_4X = 27,
KINC_G4_VERTEX_DATA_I16_4X_NORMALIZED = 28,
KINC_G4_VERTEX_DATA_U16_4X_NORMALIZED = 29,
KINC_G4_VERTEX_DATA_I32_1X = 30,
KINC_G4_VERTEX_DATA_U32_1X = 31,
KINC_G4_VERTEX_DATA_I32_2X = 32,
KINC_G4_VERTEX_DATA_U32_2X = 33,
KINC_G4_VERTEX_DATA_I32_3X = 34,
KINC_G4_VERTEX_DATA_U32_3X = 35,
KINC_G4_VERTEX_DATA_I32_4X = 36,
KINC_G4_VERTEX_DATA_U32_4X = 37,
// deprecated
KINC_G4_VERTEX_DATA_FLOAT1 = KINC_G4_VERTEX_DATA_F32_1X,
KINC_G4_VERTEX_DATA_FLOAT2 = KINC_G4_VERTEX_DATA_F32_2X,
KINC_G4_VERTEX_DATA_FLOAT3 = KINC_G4_VERTEX_DATA_F32_3X,
KINC_G4_VERTEX_DATA_FLOAT4 = KINC_G4_VERTEX_DATA_F32_4X,
KINC_G4_VERTEX_DATA_FLOAT4X4 = KINC_G4_VERTEX_DATA_F32_4X4,
KINC_G4_VERTEX_DATA_SHORT2_NORM = KINC_G4_VERTEX_DATA_I16_2X_NORMALIZED,
KINC_G4_VERTEX_DATA_SHORT4_NORM = KINC_G4_VERTEX_DATA_I16_4X_NORMALIZED,
KINC_G4_VERTEX_DATA_COLOR = KINC_G4_VERTEX_DATA_U8_4X_NORMALIZED
} kinc_g4_vertex_data_t;
static inline int kinc_g4_vertex_data_size(kinc_g4_vertex_data_t data) {
switch (data) {
default:
case KINC_G4_VERTEX_DATA_NONE:
return 0;
case KINC_G4_VERTEX_DATA_F32_1X:
return 1 * 4;
case KINC_G4_VERTEX_DATA_F32_2X:
return 2 * 4;
case KINC_G4_VERTEX_DATA_F32_3X:
return 3 * 4;
case KINC_G4_VERTEX_DATA_F32_4X:
return 4 * 4;
case KINC_G4_VERTEX_DATA_F32_4X4:
return 4 * 4 * 4;
case KINC_G4_VERTEX_DATA_I8_1X:
case KINC_G4_VERTEX_DATA_U8_1X:
case KINC_G4_VERTEX_DATA_I8_1X_NORMALIZED:
case KINC_G4_VERTEX_DATA_U8_1X_NORMALIZED:
return 1 * 1;
case KINC_G4_VERTEX_DATA_I8_2X:
case KINC_G4_VERTEX_DATA_U8_2X:
case KINC_G4_VERTEX_DATA_I8_2X_NORMALIZED:
case KINC_G4_VERTEX_DATA_U8_2X_NORMALIZED:
return 2 * 1;
case KINC_G4_VERTEX_DATA_I8_4X:
case KINC_G4_VERTEX_DATA_U8_4X:
case KINC_G4_VERTEX_DATA_I8_4X_NORMALIZED:
case KINC_G4_VERTEX_DATA_U8_4X_NORMALIZED:
return 4 * 1;
case KINC_G4_VERTEX_DATA_I16_1X:
case KINC_G4_VERTEX_DATA_U16_1X:
case KINC_G4_VERTEX_DATA_I16_1X_NORMALIZED:
case KINC_G4_VERTEX_DATA_U16_1X_NORMALIZED:
return 1 * 2;
case KINC_G4_VERTEX_DATA_I16_2X:
case KINC_G4_VERTEX_DATA_U16_2X:
case KINC_G4_VERTEX_DATA_I16_2X_NORMALIZED:
case KINC_G4_VERTEX_DATA_U16_2X_NORMALIZED:
return 2 * 2;
case KINC_G4_VERTEX_DATA_I16_4X:
case KINC_G4_VERTEX_DATA_U16_4X:
case KINC_G4_VERTEX_DATA_I16_4X_NORMALIZED:
case KINC_G4_VERTEX_DATA_U16_4X_NORMALIZED:
return 4 * 2;
case KINC_G4_VERTEX_DATA_I32_1X:
case KINC_G4_VERTEX_DATA_U32_1X:
return 1 * 4;
case KINC_G4_VERTEX_DATA_I32_2X:
case KINC_G4_VERTEX_DATA_U32_2X:
return 2 * 4;
case KINC_G4_VERTEX_DATA_I32_3X:
case KINC_G4_VERTEX_DATA_U32_3X:
return 3 * 4;
case KINC_G4_VERTEX_DATA_I32_4X:
case KINC_G4_VERTEX_DATA_U32_4X:
return 4 * 4;
}
}
typedef struct kinc_g4_vertex_element {
const char *name;
kinc_g4_vertex_data_t data;
} kinc_g4_vertex_element_t;
#define KINC_G4_MAX_VERTEX_ELEMENTS 16
typedef struct kinc_g4_vertex_structure {
kinc_g4_vertex_element_t elements[KINC_G4_MAX_VERTEX_ELEMENTS];
int size;
bool instanced;
} kinc_g4_vertex_structure_t;
/// <summary>
/// Initializes a vertex-structure.
/// </summary>
/// <param name="structure">The structure to initialize</param>
/// <returns></returns>
KINC_FUNC void kinc_g4_vertex_structure_init(kinc_g4_vertex_structure_t *structure);
/// <summary>
/// Adds an element to a vertex-structure.
/// </summary>
/// <param name="structure">The structure to add an element to</param>
/// <param name="name">The name to use for the new element</param>
/// <param name="data">The type of data to assign for the new element</param>
/// <returns></returns>
KINC_FUNC void kinc_g4_vertex_structure_add(kinc_g4_vertex_structure_t *structure, const char *name, kinc_g4_vertex_data_t data);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1 @@
#include "commandlist.h"

View File

@ -0,0 +1,323 @@
#pragma once
#include <kinc/global.h>
#include "rendertarget.h"
#include "sampler.h"
#include "texture.h"
#include "textureunit.h"
#include <kinc/backend/graphics5/commandlist.h>
#include <stddef.h>
/*! \file commandlist.h
\brief Contains functions for building command-lists to send commands to the GPU.
*/
#ifdef __cplusplus
extern "C" {
#endif
#define KINC_G5_CLEAR_COLOR 1
#define KINC_G5_CLEAR_DEPTH 2
#define KINC_G5_CLEAR_STENCIL 4
struct kinc_g5_constant_buffer;
struct kinc_g5_index_buffer;
struct kinc_g5_pipeline;
struct kinc_g5_render_target;
struct kinc_g5_texture;
struct kinc_g5_vertex_buffer;
struct kinc_g5_render_target;
/*typedef enum kinc_g5_render_target_format {
KINC_G5_RENDER_TARGET_FORMAT_32BIT,
KINC_G5_RENDER_TARGET_FORMAT_64BIT_FLOAT,
KINC_G5_RENDER_TARGET_FORMAT_32BIT_RED_FLOAT,
KINC_G5_RENDER_TARGET_FORMAT_128BIT_FLOAT,
KINC_G5_RENDER_TARGET_FORMAT_16BIT_DEPTH,
KINC_G5_RENDER_TARGET_FORMAT_8BIT_RED
} kinc_g5_render_target_format_t;*/
// typedef kinc_g4_render_target_format_t kinc_g5_render_target_format_t;
typedef struct kinc_g5_command_list {
CommandList5Impl impl;
} kinc_g5_command_list_t;
/// <summary>
/// Initializes a command-list.
/// </summary>
/// <param name="list">The command-list to initialize</param>
KINC_FUNC void kinc_g5_command_list_init(kinc_g5_command_list_t *list);
/// <summary>
/// Destroys a command-list.
/// </summary>
/// <param name="list">The command-list to destroy</param>
KINC_FUNC void kinc_g5_command_list_destroy(kinc_g5_command_list_t *list);
/// <summary>
/// Starts recording commands in a command-list.
/// </summary>
/// <param name="list">The list to use</param>
KINC_FUNC void kinc_g5_command_list_begin(kinc_g5_command_list_t *list);
/// <summary>
/// Ends recording commands for the list. Has to be called after kinc_g5_command_list_begin and before kinc_g5_command_list_execute.
/// </summary>
/// <param name="list"></param>
/// <returns></returns>
KINC_FUNC void kinc_g5_command_list_end(kinc_g5_command_list_t *list);
/// <summary>
/// Records a command to clear the color, depth and/or stencil-components of a render-target.
/// </summary>
/// <param name="list">The list to write the command to</param>
/// <param name="render_target">The render-target to clear</param>
/// <param name="flags">Defines what components to clear</param>
/// <param name="color">The color-value to clear to</param>
/// <param name="depth">The depth-value to clear to</param>
/// <param name="stencil">The stencil-value to clear to</param>
KINC_FUNC void kinc_g5_command_list_clear(kinc_g5_command_list_t *list, struct kinc_g5_render_target *render_target, unsigned flags, unsigned color,
float depth, int stencil);
/// <summary>
/// Records a command that prepares a render-target to be used as the current framebuffer.
/// </summary>
/// <param name="list">The list to write the command to</param>
/// <param name="renderTarget">The render-target to use as the current framebuffer</param>
KINC_FUNC void kinc_g5_command_list_render_target_to_framebuffer_barrier(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget);
/// <summary>
/// Records a command that prepares a render-target for regular render-target-usage after being used as the current framebuffer.
/// </summary>
/// <param name="list">The list to write the command to</param>
/// <param name="renderTarget">The render-target to use in regular render-target-mode</param>
KINC_FUNC void kinc_g5_command_list_framebuffer_to_render_target_barrier(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget);
/// <summary>
/// Writes a command that prepares a render-target to be rendered to.
/// </summary>
/// <param name="list">The list to write the command to</param>
/// <param name="renderTarget">The render-target to render to</param>
KINC_FUNC void kinc_g5_command_list_texture_to_render_target_barrier(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget);
/// <summary>
/// Writes a command that prepares a render-target to be used for sampling/reading like a texture.
/// </summary>
/// <param name="list">The list to write the command to</param>
/// <param name="renderTarget">The render-target to be used like a texture</param>
KINC_FUNC void kinc_g5_command_list_render_target_to_texture_barrier(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget);
/// <summary>
/// Writes a command that draws the entire content of the currently set index-buffer and vertex-buffer. G4 can only draw triangle-lists using vertex-indices as
/// this is what GPUs tend to be optimized for.
/// </summary>
/// <param name="list">The list to write the command to</param>
KINC_FUNC void kinc_g5_command_list_draw_indexed_vertices(kinc_g5_command_list_t *list);
/// <summary>
/// Writes a command that draws a part of the content of the currently set index-buffer and vertex-buffer. G4 can only draw triangle-lists using vertex-indices
/// as this is what GPUs tend to be optimized for.
/// </summary>
/// <param name="list">The list to write the command to</param>
/// <param name="start">The offset into the index-buffer</param>
/// <param name="count">The number of indices to use</param>
KINC_FUNC void kinc_g5_command_list_draw_indexed_vertices_from_to(kinc_g5_command_list_t *list, int start, int count);
/// <summary>
/// Writes a command that draws a part of the content of the currently set index-buffer and vertex-buffer and additionally applies a general offset into the
/// vertex-buffer. G4 can only draw triangle-lists using vertex-indices as this is what GPUs tend to be optimized for.
/// </summary>
/// <param name="list">The list to write the command to</param>
/// <param name="start">The offset into the index-buffer</param>
/// <param name="count">The number of indices to use</param>
/// <param name="vertex_offset">The offset into the vertex-buffer which is added to each index read from the index-buffer</param>
KINC_FUNC void kinc_g5_command_list_draw_indexed_vertices_from_to_from(kinc_g5_command_list_t *list, int start, int count, int vertex_offset);
KINC_FUNC void kinc_g5_command_list_draw_indexed_vertices_instanced(kinc_g5_command_list_t *list, int instanceCount);
KINC_FUNC void kinc_g5_command_list_draw_indexed_vertices_instanced_from_to(kinc_g5_command_list_t *list, int instanceCount, int start, int count);
/// <summary>
/// Writes a command that sets the viewport which defines the portion of the framebuffer or render-target things are rendered into. By default the viewport is
/// equivalent to the full size of the current render-target or framebuffer.
/// </summary>
/// <param name="list">The list to write the command to</param>
/// <param name="x">The x-offset of the viewport from the left of the screen in pixels</param>
/// <param name="y">The y-offset of the viewport from the top of the screen in pixels</param>
/// <param name="width">The width of the viewport in pixels</param>
/// <param name="height">The height of the viewport in pixels</param>
KINC_FUNC void kinc_g5_command_list_viewport(kinc_g5_command_list_t *list, int x, int y, int width, int height);
/// <summary>
/// Writes a command that enables and defines the scissor-rect. When the scissor-rect is enabled, anything that's rendered outside of the scissor-rect will be
/// ignored.
/// </summary>
/// <param name="list">The list to write the command to</param>
/// <param name="x">The x-offset of the scissor-rect from the left of the screen in pixels</param>
/// <param name="y">The y-offset of the scissor-rect from the top of the screen in pixels</param>
/// <param name="width">The width of the scissor-rect in pixels</param>
/// <param name="height">The height of the scissor-rect in pixels</param>
KINC_FUNC void kinc_g5_command_list_scissor(kinc_g5_command_list_t *list, int x, int y, int width, int height);
/// <summary>
/// Writes a command to disable the scissor-rect.
/// </summary>
/// <param name="list">The list to write the command to</param>
KINC_FUNC void kinc_g5_command_list_disable_scissor(kinc_g5_command_list_t *list);
/// <summary>
/// Writes a command to set the pipeline for the next draw-call. The pipeline defines most rendering-state including the shaders to be used.
/// </summary>
/// <param name="list">The list to write the command to</param>
/// <param name="pipeline">The pipeline to set</param>
KINC_FUNC void kinc_g5_command_list_set_pipeline(kinc_g5_command_list_t *list, struct kinc_g5_pipeline *pipeline);
/// <summary>
/// Sets the blend constant used for `KINC_G5_BLEND_CONSTANT` or `KINC_G5_INV_BLEND_CONSTANT`
/// </summary>
KINC_FUNC void kinc_g5_command_list_set_blend_constant(kinc_g5_command_list_t *list, float r, float g, float b, float a);
/// <summary>
/// Writes a command which sets vertex-buffers for the next draw-call.
/// </summary>
/// <param name="list">The list to write the command to</param>
/// <param name="buffers">The buffers to set</param>
/// <param name="offsets">The offset to use for every buffer in number of vertices</param>
/// <param name="count">The number of buffers to set</param>
KINC_FUNC void kinc_g5_command_list_set_vertex_buffers(kinc_g5_command_list_t *list, struct kinc_g5_vertex_buffer **buffers, int *offsets, int count);
/// <summary>
/// Writes a command to set an index-buffer to be used for the next draw-command.
/// </summary>
/// <param name="list">The list to write the command to</param>
/// <param name="buffer">The buffer to use</param>
KINC_FUNC void kinc_g5_command_list_set_index_buffer(kinc_g5_command_list_t *list, struct kinc_g5_index_buffer *buffer);
/// <summary>
/// Writes a command that sets the render-targets to draw into in following draw-calls.
/// </summary>
/// <param name="list">The list to write the command to</param>
/// <param name="targets">The render-targets to use for following-draw calls</param>
/// <param name="count">The number of render-targets to use</param>
KINC_FUNC void kinc_g5_command_list_set_render_targets(kinc_g5_command_list_t *list, struct kinc_g5_render_target **targets, int count);
/// <summary>
/// Writes a command to upload an index-buffer that's in main-memory to gpu-memory. Does nothing on unified-memory-systems.
/// </summary>
/// <param name="list">The list to write the command to</param>
/// <param name="buffer">The buffer to upload</param>
KINC_FUNC void kinc_g5_command_list_upload_index_buffer(kinc_g5_command_list_t *list, struct kinc_g5_index_buffer *buffer);
/// <summary>
/// Writes a command to upload a vertex-buffer that's in main-memory to gpu-memory. Does nothing on unified-memory-systems.
/// </summary>
/// <param name="list">The list to write the command to</param>
/// <param name="buffer">The buffer to upload</param>
KINC_FUNC void kinc_g5_command_list_upload_vertex_buffer(kinc_g5_command_list_t *list, struct kinc_g5_vertex_buffer *buffer);
/// <summary>
/// Writes a command to upload a texture that's in main-memory to gpu-memory. Does nothing on unified-memory-systems.
/// </summary>
/// <param name="list">The list to write the command to</param>
/// <param name="buffer">The texture to upload</param>
KINC_FUNC void kinc_g5_command_list_upload_texture(kinc_g5_command_list_t *list, struct kinc_g5_texture *texture);
/// <summary>
/// Writes a command that sets a constant-buffer for the vertex-shader-stage.
/// </summary>
/// <param name="list">The list to write the command to</param>
/// <param name="buffer">The buffer to set</param>
/// <param name="offset">The offset into the buffer in bytes to use as the start</param>
/// <param name="size">The size of the buffer to use in bytes starting at the offset</param>
KINC_FUNC void kinc_g5_command_list_set_vertex_constant_buffer(kinc_g5_command_list_t *list, struct kinc_g5_constant_buffer *buffer, int offset, size_t size);
/// <summary>
/// Writes a command that sets a constant-buffer for the fragment-shader-stage.
/// </summary>
/// <param name="list">The list to write the command to</param>
/// <param name="buffer">The buffer to set</param>
/// <param name="offset">The offset into the buffer in bytes to use as the start</param>
/// <param name="size">The size of the buffer to use in bytes starting at the offset</param>
KINC_FUNC void kinc_g5_command_list_set_fragment_constant_buffer(kinc_g5_command_list_t *list, struct kinc_g5_constant_buffer *buffer, int offset, size_t size);
/// <summary>
/// Kicks off execution of the commands which have been recorded in the command-list. kinc_g5_command_list_end has to be called beforehand.
/// </summary>
/// <param name="list">The command-list to execute</param>
KINC_FUNC void kinc_g5_command_list_execute(kinc_g5_command_list_t *list);
/// <summary>
/// Waits for execution of the command_list to finish. Make sure the command-list is executing before you wait for it.
/// Also take note that waiting for a command-list to finish executing completely is a very expensive operation.
/// </summary>
/// <param name="list">The command-list to execute</param>
KINC_FUNC void kinc_g5_command_list_wait_for_execution_to_finish(kinc_g5_command_list_t *list);
/// <summary>
/// Writes a command that copies the contents of a render-target into a cpu-side buffer. Beware: This is enormously slow.
/// </summary>
/// <param name="list">The list to write the command to</param>
/// <param name="render_target">The render-target to copy the data from</param>
/// <param name="data">The buffer to copy the data into</param>
KINC_FUNC void kinc_g5_command_list_get_render_target_pixels(kinc_g5_command_list_t *list, struct kinc_g5_render_target *render_target, uint8_t *data);
/// <summary>
/// Records a command that fires off a compute-run on x * y * z elements.
/// </summary>
/// <param name="list">The list to write the command to</param>
/// <param name="x">The x-size for the compute-run</param>
/// <param name="y">The y-size for the compute-run</param>
/// <param name="z">The z-size for the compute-run</param>
KINC_FUNC void kinc_g5_command_list_compute(kinc_g5_command_list_t *list, int x, int y, int z);
/// <summary>
/// Assigns a texture to a texture-unit for sampled access via GLSL's texture.
/// </summary>
/// <param name="list">The list to write the command to</param>
/// <param name="unit">The unit to assign this texture to</param>
/// <param name="texture">The texture to assign to the unit</param>
KINC_FUNC void kinc_g5_command_list_set_texture(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_texture_t *texture);
/// <summary>
/// Assigns a texture to a texture-unit for direct access via GLSL's texelFetch (as
/// opposed to GLSL's texture). The name of this functions is unfortunately based
/// on OpenGL's confusing terminology.
/// </summary>
/// <param name="list">The list to write the command to</param>
/// <param name="unit">The unit to assign this texture to</param>
/// <param name="texture">The texture to assign to the unit</param>
KINC_FUNC void kinc_g5_command_list_set_image_texture(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_texture_t *texture);
/// <summary>
/// Uses the color-component of a render-target as a texture.
/// </summary>
/// <param name="list">The list to write the command to</param>
/// <param name="unit">The texture-unit to assign the render-target to</param>
/// <param name="target">The render-target to use</param>
KINC_FUNC void kinc_g5_command_list_set_texture_from_render_target(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_render_target_t *target);
/// <summary>
/// Uses the depth-component of a render-target as a texture.
/// </summary>
/// <param name="list">The list to write the command to</param>
/// <param name="unit">The texture-unit to assign the render-target to</param>
/// <param name="target">The render-target to use</param>
KINC_FUNC void kinc_g5_command_list_set_texture_from_render_target_depth(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit,
kinc_g5_render_target_t *target);
KINC_FUNC void kinc_g5_command_list_set_render_target_face(kinc_g5_command_list_t *list, kinc_g5_render_target_t *texture, int face);
KINC_FUNC void kinc_g5_command_list_set_sampler(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_sampler_t *sampler);
// Occlusion Query
KINC_FUNC bool kinc_g5_command_list_init_occlusion_query(kinc_g5_command_list_t *list, unsigned *occlusionQuery);
KINC_FUNC void kinc_g5_command_list_delete_occlusion_query(kinc_g5_command_list_t *list, unsigned occlusionQuery);
KINC_FUNC void kinc_g5_command_list_render_occlusion_query(kinc_g5_command_list_t *list, unsigned occlusionQuery, int triangles);
KINC_FUNC bool kinc_g5_command_list_are_query_results_available(kinc_g5_command_list_t *list, unsigned occlusionQuery);
KINC_FUNC void kinc_g5_command_list_get_query_result(kinc_g5_command_list_t *list, unsigned occlusionQuery, unsigned *pixelCount);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,58 @@
#pragma once
#include <kinc/global.h>
#include <Kore/ComputeImpl.h>
#include <kinc/graphics5/constantlocation.h>
#include <kinc/graphics5/textureunit.h>
/*! \file compute.h
\brief Provides support for running compute-shaders.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef struct kinc_g5_compute_shader_impl {
int a;
} kinc_g5_compute_shader_<EFBFBD>mpl_t;
typedef struct kinc_g5_compute_shader {
kinc_g5_compute_shader_<EFBFBD>mpl_t impl;
} kinc_g5_compute_shader_t;
/// <summary>
/// Initialize a compute-shader from system-specific shader-data.
/// </summary>
/// <param name="shader">The shader-object to initialize</param>
/// <param name="source">A pointer to system-specific shader-data</param>
/// <param name="length">Length of the shader-data in bytes</param>
KINC_FUNC void kinc_g5_compute_shader_init(kinc_g5_compute_shader_t *shader, void *source, int length);
/// <summary>
/// Desotry a shader-object
/// </summary>
/// <param name="shader">The shader-object to destroy</param>
KINC_FUNC void kinc_g5_compute_shader_destroy(kinc_g5_compute_shader_t *shader);
/// <summary>
/// Finds the location of a constant/uniform inside of a shader.
/// </summary>
/// <param name="shader">The shader to look into</param>
/// <param name="name">The constant/uniform-name to look for</param>
/// <returns>The found constant-location</returns>
KINC_FUNC kinc_g5_constant_location_t kinc_g5_compute_shader_get_constant_location(kinc_g5_compute_shader_t *shader, const char *name);
/// <summary>
/// Finds a texture-unit inside of a shader.
/// </summary>
/// <param name="shader">The shader to look into</param>
/// <param name="name">The texture-name to look for</param>
/// <returns>The found texture-unit</returns>
KINC_FUNC kinc_g5_texture_unit_t kinc_compute_g5_shader_get_texture_unit(kinc_g5_compute_shader_t *shader, const char *name);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,112 @@
#include "constantbuffer.h"
static void setInt(uint8_t *constants, int offset, int value) {
int *ints = (int *)(&constants[offset]);
ints[0] = value;
}
static void setFloat(uint8_t *constants, int offset, float value) {
float *floats = (float *)(&constants[offset]);
floats[0] = value;
}
static void setFloat2(uint8_t *constants, int offset, float value1, float value2) {
float *floats = (float *)(&constants[offset]);
floats[0] = value1;
floats[1] = value2;
}
static void setFloat3(uint8_t *constants, int offset, float value1, float value2, float value3) {
float *floats = (float *)(&constants[offset]);
floats[0] = value1;
floats[1] = value2;
floats[2] = value3;
}
static void setFloat4(uint8_t *constants, int offset, float value1, float value2, float value3, float value4) {
float *floats = (float *)(&constants[offset]);
floats[0] = value1;
floats[1] = value2;
floats[2] = value3;
floats[3] = value4;
}
static void setFloats(uint8_t *constants, int offset, float *values, int count) {
float *floats = (float *)(&constants[offset]);
for (int i = 0; i < count; ++i) {
floats[i] = values[i];
}
}
static void setBool(uint8_t *constants, int offset, bool value) {
int *ints = (int *)(&constants[offset]);
ints[0] = value ? 1 : 0;
}
static void setMatrix4(uint8_t *constants, int offset, kinc_matrix4x4_t *value) {
float *floats = (float *)(&constants[offset]);
for (int y = 0; y < 4; ++y) {
for (int x = 0; x < 4; ++x) {
floats[x + y * 4] = kinc_matrix4x4_get(value, x, y);
}
}
}
static void setMatrix3(uint8_t *constants, int offset, kinc_matrix3x3_t *value) {
float *floats = (float *)(&constants[offset]);
for (int y = 0; y < 3; ++y) {
for (int x = 0; x < 3; ++x) {
floats[x + y * 4] = kinc_matrix3x3_get(value, x, y);
}
}
}
void kinc_g5_constant_buffer_set_int(kinc_g5_constant_buffer_t *buffer, int offset, int value) {
setInt(buffer->data, offset, value);
}
void kinc_g5_constant_buffer_set_float(kinc_g5_constant_buffer_t *buffer, int offset, float value) {
setFloat(buffer->data, offset, value);
}
void kinc_g5_constant_buffer_set_float2(kinc_g5_constant_buffer_t *buffer, int offset, float value1, float value2) {
setFloat2(buffer->data, offset, value1, value2);
}
void kinc_g5_constant_buffer_set_float3(kinc_g5_constant_buffer_t *buffer, int offset, float value1, float value2, float value3) {
setFloat3(buffer->data, offset, value1, value2, value3);
}
void kinc_g5_constant_buffer_set_float4(kinc_g5_constant_buffer_t *buffer, int offset, float value1, float value2, float value3, float value4) {
setFloat4(buffer->data, offset, value1, value2, value3, value4);
}
void kinc_g5_constant_buffer_set_floats(kinc_g5_constant_buffer_t *buffer, int offset, float *values, int count) {
setFloats(buffer->data, offset, values, count);
}
void kinc_g5_constant_buffer_set_bool(kinc_g5_constant_buffer_t *buffer, int offset, bool value) {
setBool(buffer->data, offset, value);
}
void kinc_g5_constant_buffer_set_matrix4(kinc_g5_constant_buffer_t *buffer, int offset, kinc_matrix4x4_t *value) {
if (kinc_g5_transposeMat4) {
kinc_matrix4x4_t m = *value;
kinc_matrix4x4_transpose(&m);
setMatrix4(buffer->data, offset, &m);
}
else {
setMatrix4(buffer->data, offset, value);
}
}
void kinc_g5_constant_buffer_set_matrix3(kinc_g5_constant_buffer_t *buffer, int offset, kinc_matrix3x3_t *value) {
if (kinc_g5_transposeMat3) {
kinc_matrix3x3_t m = *value;
kinc_matrix3x3_transpose(&m);
setMatrix3(buffer->data, offset, &m);
}
else {
setMatrix3(buffer->data, offset, value);
}
}

View File

@ -0,0 +1,140 @@
#pragma once
#include <kinc/global.h>
#include <kinc/backend/graphics5/constantbuffer.h>
#include <kinc/math/matrix.h>
#include <kinc/math/vector.h>
/*! \file constantbuffer.h
\brief Provides support for managing buffers of constant-data for shaders.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef struct kinc_g5_constant_buffer {
uint8_t *data;
ConstantBuffer5Impl impl;
} kinc_g5_constant_buffer_t;
/// <summary>
/// Initializes a constant-buffer.
/// </summary>
/// <param name="buffer">The buffer to initialize</param>
/// <param name="size">The size of the constant-data in the buffer in bytes</param>
KINC_FUNC void kinc_g5_constant_buffer_init(kinc_g5_constant_buffer_t *buffer, int size);
/// <summary>
/// Destroys a buffer.
/// </summary>
/// <param name="buffer">The buffer to destroy</param>
KINC_FUNC void kinc_g5_constant_buffer_destroy(kinc_g5_constant_buffer_t *buffer);
/// <summary>
/// Locks all of a constant-buffer to modify its contents.
/// </summary>
/// <param name="buffer">The buffer to lock</param>
/// <returns>The contents of the buffer</returns>
KINC_FUNC void kinc_g5_constant_buffer_lock_all(kinc_g5_constant_buffer_t *buffer);
/// <summary>
/// Locks part of a constant-buffer to modify its contents.
/// </summary>
/// <param name="buffer">The buffer to lock</param>
/// <param name="start">The offset of where to start the lock in bytes</param>
/// <param name="count">The number of bytes to lock</param>
/// <returns>The contents of the buffer, starting at start</returns>
KINC_FUNC void kinc_g5_constant_buffer_lock(kinc_g5_constant_buffer_t *buffer, int start, int count);
/// <summary>
/// Unlocks a constant-buffer so the changed contents can be used.
/// </summary>
/// <param name="buffer">The buffer to unlock</param>
KINC_FUNC void kinc_g5_constant_buffer_unlock(kinc_g5_constant_buffer_t *buffer);
/// <summary>
/// Figures out the size of the constant-data in the buffer.
/// </summary>
/// <param name="buffer">The buffer to figure out the size for</param>
/// <returns>Returns the size of the constant-data in the buffer in bytes</returns>
KINC_FUNC int kinc_g5_constant_buffer_size(kinc_g5_constant_buffer_t *buffer);
/// <summary>
/// Assigns a bool at an offset in a constant-buffer.
/// </summary>
/// <param name="offset">The offset at which to write the data</param>
/// <param name="value">The value to write into the buffer</param>
KINC_FUNC void kinc_g5_constant_buffer_set_bool(kinc_g5_constant_buffer_t *buffer, int offset, bool value);
/// <summary>
/// Assigns an integer at an offset in a constant-buffer.
/// </summary>
/// <param name="offset">The offset at which to write the data</param>
/// <param name="value">The value to assign to the constant/uniform</param>
KINC_FUNC void kinc_g5_constant_buffer_set_int(kinc_g5_constant_buffer_t *buffer, int offset, int value);
/// <summary>
/// Assigns a float at an offset in a constant-buffer.
/// </summary>
/// <param name="offset">The offset at which to write the data</param>
/// <param name="value">The value to write into the buffer</param>
KINC_FUNC void kinc_g5_constant_buffer_set_float(kinc_g5_constant_buffer_t *buffer, int offset, float value);
/// <summary>
/// Assigns two floats at an offset in a constant-buffer.
/// </summary>
/// <param name="offset">The offset at which to write the data</param>
/// <param name="value1">The first value to write into the buffer</param>
/// <param name="value2">The second value to write into the buffer</param>
KINC_FUNC void kinc_g5_constant_buffer_set_float2(kinc_g5_constant_buffer_t *buffer, int offset, float value1, float value2);
/// <summary>
/// Assigns three floats at an offset in a constant-buffer.
/// </summary>
/// <param name="offset">The offset at which to write the data</param>
/// <param name="value1">The first value to write into the buffer</param>
/// <param name="value2">The second value to write into the buffer</param>
/// <param name="value3">The third value to write into the buffer/param>
KINC_FUNC void kinc_g5_constant_buffer_set_float3(kinc_g5_constant_buffer_t *buffer, int offset, float value1, float value2, float value3);
/// <summary>
/// Assigns four floats at an offset in a constant-buffer.
/// </summary>
/// <param name="offset">The offset at which to write the data</param>
/// <param name="value1">The first value to write into the buffer</param>
/// <param name="value2">The second value to write into the buffer</param>
/// <param name="value3">The third value to write into the buffer/param>
/// <param name="value4">The fourth value to write into the buffer</param>
KINC_FUNC void kinc_g5_constant_buffer_set_float4(kinc_g5_constant_buffer_t *buffer, int offset, float value1, float value2, float value3, float value4);
/// <summary>
/// Assigns a bunch of floats at an offset in a constant-buffer.
/// </summary>
/// <param name="location">The location of the constant/uniform to assign the values to</param>
/// <param name="value">The values to write into the buffer</param>
/// <param name="value">The number of values to write into the buffer</param>
KINC_FUNC void kinc_g5_constant_buffer_set_floats(kinc_g5_constant_buffer_t *buffer, int offset, float *values, int count);
/// <summary>
/// Assigns a 3x3-matrix at an offset in a constant-buffer.
/// </summary>
/// <param name="offset">The offset at which to write the data</param>
/// <param name="value">The value to write into the buffer</param>
KINC_FUNC void kinc_g5_constant_buffer_set_matrix3(kinc_g5_constant_buffer_t *buffer, int offset, kinc_matrix3x3_t *value);
/// <summary>
/// Assigns a 4x4-matrix at an offset in a constant-buffer.
/// </summary>
/// <param name="offset">The offset at which to write the data</param>
/// <param name="value">The value to write into the buffer</param>
KINC_FUNC void kinc_g5_constant_buffer_set_matrix4(kinc_g5_constant_buffer_t *buffer, int offset, kinc_matrix4x4_t *value);
KINC_FUNC extern bool kinc_g5_transposeMat3;
KINC_FUNC extern bool kinc_g5_transposeMat4;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,21 @@
#pragma once
#include <kinc/global.h>
#include <kinc/backend/graphics5/pipeline.h>
/*! \file constantlocation.h
\brief Provides the constant_location-struct which is used for setting constants/uniforms in a shader.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef struct kinc_g5_constant_location {
ConstantLocation5Impl impl;
} kinc_g5_constant_location_t;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,7 @@
#include "commandlist.c.h"
#include "constantbuffer.c.h"
#include "graphics.c.h"
#include "pipeline.c.h"
#include "rendertarget.c.h"
#include "sampler.c.h"
#include "texture.c.h"

View File

@ -0,0 +1,22 @@
#ifndef OPENGL_1_X
#include "graphics.h"
static int samples = 1;
int kinc_g5_antialiasing_samples(void) {
return samples;
}
void kinc_g5_set_antialiasing_samples(int samples_) {
samples = samples_;
}
bool kinc_g5_fullscreen = false;
// void Graphics5::setVertexBuffer(VertexBuffer& vertexBuffer) {
// VertexBuffer* vertexBuffers[1] = {&vertexBuffer};
// setVertexBuffers(vertexBuffers, 1);
//}
#endif

View File

@ -0,0 +1,113 @@
#pragma once
#include <kinc/global.h>
#include <kinc/image.h>
#include "rendertarget.h"
#include "shader.h"
#include "vertexstructure.h"
#include <kinc/backend/graphics5/graphics.h>
#include <kinc/math/matrix.h>
#include <kinc/math/vector.h>
/*! \file graphics.h
\brief Contains the base G5-functionality.
*/
#ifdef __cplusplus
extern "C" {
#endif
KINC_FUNC extern bool kinc_g5_fullscreen;
/// <summary>
/// Returns whether raytracing (see kinc/graphics5/raytrace.h) is supported.
/// </summary>
/// <returns>Whether raytracing is supported</returns>
KINC_FUNC bool kinc_g5_supports_raytracing(void);
/// <summary>
/// Returns whether instanced rendering (kinc_g5_command_list_draw_indexed_vertices_instanced and pals) is supported.
/// </summary>
/// <returns>Whether instanced rendering is supported</returns>
KINC_FUNC bool kinc_g5_supports_instanced_rendering(void);
/// <summary>
/// Returns whether GPU-compute (the functions in kinc/graphics5/compute.h) is supported.
/// </summary>
/// <returns>Whether GPU-compute is supported</returns>
KINC_FUNC bool kinc_g5_supports_compute_shaders(void);
/// <summary>
/// Returns whether blend-constants (see kinc_g4_set_blend_constant and the blending-properties for pipelines) are supported.
/// </summary>
/// <returns>Whether blend-constants are supported</returns>
KINC_FUNC bool kinc_g5_supports_blend_constants(void);
/// <summary>
/// Returns whether textures are supported which have widths/heights which are not powers of two.
/// </summary>
/// <returns>Whether non power of two texture-sizes are supported</returns>
KINC_FUNC bool kinc_g5_supports_non_pow2_textures(void);
/// <summary>
/// Returns whether render-targets are upside down. This happens in OpenGL and there is currently no automatic mitigation.
/// </summary>
/// <returns>Whether render-targets are upside down</returns>
KINC_FUNC bool kinc_g5_render_targets_inverted_y(void);
/// <summary>
/// Returns how many textures can be used at the same time in a fragment-shader.
/// </summary>
/// <returns>The number of textures</returns>
KINC_FUNC int kinc_g5_max_bound_textures(void);
/// <summary>
/// I think this does nothing.
/// </summary>
KINC_FUNC void kinc_g5_flush(void);
/// <summary>
/// Returns the currently used number of samples for hardware-antialiasing.
/// </summary>
/// <returns>The number of samples</returns>
KINC_FUNC int kinc_g5_antialiasing_samples(void);
/// <summary>
/// Sets the number of samples used for hardware-antialiasing. This typically uses multisampling and typically only works with a few specific numbers of
/// sample-counts - 2 and 4 are pretty safe bets. It also might do nothing at all.
/// </summary>
/// <param name="samples">The number of samples</param>
KINC_FUNC void kinc_g5_set_antialiasing_samples(int samples);
/// <summary>
/// Needs to be called before rendering to a window. Typically called at the start of each frame.
/// </summary>
/// <param name="window">The window to render to</param>
KINC_FUNC void kinc_g5_begin(kinc_g5_render_target_t *renderTarget, int window);
/// <summary>
/// Needs to be called after rendering to a window. Typically called at the end of each frame.
/// </summary>
/// <param name="window">The window to render to</param>
/// <returns></returns>
KINC_FUNC void kinc_g5_end(int window);
/// <summary>
/// Needs to be called to make the rendered frame visible. Typically called at the very end of each frame.
/// </summary>
KINC_FUNC bool kinc_g5_swap_buffers(void);
#ifndef KINC_DOCS
void kinc_g5_internal_init(void);
void kinc_g5_internal_init_window(int window, int depth_buffer_bits, int stencil_buffer_bits, bool vsync);
void kinc_g5_internal_destroy_window(int window);
void kinc_g5_internal_destroy(void);
#endif
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,73 @@
#pragma once
#include <kinc/global.h>
#include <kinc/backend/graphics5/indexbuffer.h>
/*! \file indexbuffer.h
\brief Provides functions for setting up and using index-buffers.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef enum kinc_g5_index_buffer_format { KINC_G5_INDEX_BUFFER_FORMAT_32BIT, KINC_G5_INDEX_BUFFER_FORMAT_16BIT } kinc_g5_index_buffer_format_t;
typedef struct kinc_g5_index_buffer {
IndexBuffer5Impl impl;
} kinc_g5_index_buffer_t;
/// <summary>
/// Initializes an index-buffer.
/// </summary>
/// <param name="buffer">The buffer to initialize</param>
/// <param name="count">The number of indices to allocate for the buffer</param>
/// <param name="gpu_memory">When true, the buffer will be uploaded to gpu-memory which will make it faster to use but slower to change</param>
KINC_FUNC void kinc_g5_index_buffer_init(kinc_g5_index_buffer_t *buffer, int count, kinc_g5_index_buffer_format_t format, bool gpu_memory);
/// <summary>
/// Destroys an index-buffer.
/// </summary>
/// <param name="buffer">The buffer to destroy</param>
KINC_FUNC void kinc_g5_index_buffer_destroy(kinc_g5_index_buffer_t *buffer);
/// <summary>
/// Locks an index-buffer so its contents can be modified.
/// </summary>
/// <param name="buffer">The buffer to lock</param>
/// <returns>The contents of the index-buffer in uint32s or uint16s depending on the format provided when initializing</returns>
KINC_FUNC void *kinc_g5_index_buffer_lock_all(kinc_g5_index_buffer_t *buffer);
/// <summary>
/// Locks part of a vertex-buffer to modify its contents.
/// </summary>
/// <param name="buffer">The buffer to lock</param>
/// <param name="start">The index of the first index to lock</param>
/// <param name="count">The number of indices to lock</param>
/// <returns>The contents of the index-buffer, starting at start, in uint32s or uint16s depending on the format provided when initializing</returns>
KINC_FUNC void *kinc_g5_index_buffer_lock(kinc_g5_index_buffer_t *buffer, int start, int count);
/// <summary>
/// Unlocks an index-buffer after locking it so the changed buffer-contents can be used.
/// </summary>
/// <param name="buffer">The buffer to unlock</param>
KINC_FUNC void kinc_g5_index_buffer_unlock_all(kinc_g5_index_buffer_t *buffer);
/// <summary>
/// Unlocks part of an index-buffer after locking so the changed buffer-contents can be used.
/// </summary>
/// <param name="buffer">The buffer to unlock</param>
/// <param name="count">The number of indices to unlock, starting from the start-index from the previous lock-call</param>
KINC_FUNC void kinc_g5_index_buffer_unlock(kinc_g5_index_buffer_t *buffer, int count);
/// <summary>
/// Returns the number of indices in the buffer.
/// </summary>
/// <param name="buffer">The buffer to query for its number of indices</param>
/// <returns>The number of indices</returns>
KINC_FUNC int kinc_g5_index_buffer_count(kinc_g5_index_buffer_t *buffer);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,51 @@
#include "pipeline.h"
void kinc_g5_internal_pipeline_init(kinc_g5_pipeline_t *pipe) {
for (int i = 0; i < 16; ++i)
pipe->inputLayout[i] = NULL;
pipe->vertexShader = NULL;
pipe->fragmentShader = NULL;
pipe->geometryShader = NULL;
pipe->tessellationControlShader = NULL;
pipe->tessellationEvaluationShader = NULL;
pipe->cullMode = KINC_G5_CULL_MODE_NEVER;
pipe->depthWrite = false;
pipe->depthMode = KINC_G5_COMPARE_MODE_ALWAYS;
pipe->stencilMode = KINC_G5_COMPARE_MODE_ALWAYS;
pipe->stencilBothPass = KINC_G5_STENCIL_ACTION_KEEP;
pipe->stencilDepthFail = KINC_G5_STENCIL_ACTION_KEEP;
pipe->stencilFail = KINC_G5_STENCIL_ACTION_KEEP;
pipe->stencilReferenceValue = 0;
pipe->stencilReadMask = 0xff;
pipe->stencilWriteMask = 0xff;
pipe->blend_source = KINC_G5_BLEND_ONE;
pipe->blend_destination = KINC_G5_BLEND_ZERO;
pipe->blend_operation = KINC_G5_BLENDOP_ADD;
pipe->alpha_blend_source = KINC_G5_BLEND_ONE;
pipe->alpha_blend_destination = KINC_G5_BLEND_ZERO;
pipe->alpha_blend_operation = KINC_G5_BLENDOP_ADD;
for (int i = 0; i < 8; ++i)
pipe->colorWriteMaskRed[i] = true;
for (int i = 0; i < 8; ++i)
pipe->colorWriteMaskGreen[i] = true;
for (int i = 0; i < 8; ++i)
pipe->colorWriteMaskBlue[i] = true;
for (int i = 0; i < 8; ++i)
pipe->colorWriteMaskAlpha[i] = true;
pipe->colorAttachmentCount = 1;
for (int i = 0; i < 8; ++i)
pipe->colorAttachment[i] = KINC_G5_RENDER_TARGET_FORMAT_32BIT;
pipe->depthAttachmentBits = 0;
pipe->stencilAttachmentBits = 0;
}
void kinc_g5_internal_compute_pipeline_init(kinc_g5_compute_pipeline_t *pipe) {
pipe->compute_shader = NULL;
}

View File

@ -0,0 +1,199 @@
#pragma once
#include <kinc/global.h>
#include <kinc/backend/graphics5/pipeline.h>
#include <kinc/graphics5/vertexstructure.h>
#include "constantlocation.h"
#include "graphics.h"
/*! \file pipeline.h
\brief Provides functions for creating and using pipelines which configure the GPU for rendering.
*/
#ifdef __cplusplus
extern "C" {
#endif
struct kinc_g5_shader;
// identical to kinc_g4_blending_factor_t
typedef enum {
KINC_G5_BLEND_ONE,
KINC_G5_BLEND_ZERO,
KINC_G5_BLEND_SOURCE_ALPHA,
KINC_G5_BLEND_DEST_ALPHA,
KINC_G5_BLEND_INV_SOURCE_ALPHA,
KINC_G5_BLEND_INV_DEST_ALPHA,
KINC_G5_BLEND_SOURCE_COLOR,
KINC_G5_BLEND_DEST_COLOR,
KINC_G5_BLEND_INV_SOURCE_COLOR,
KINC_G5_BLEND_INV_DEST_COLOR,
KINC_G5_BLEND_CONSTANT,
KINC_G5_BLEND_INV_CONSTANT
} kinc_g5_blending_factor_t;
// identical to kinc_g4_blending_operation_t
typedef enum {
KINC_G5_BLENDOP_ADD,
KINC_G5_BLENDOP_SUBTRACT,
KINC_G5_BLENDOP_REVERSE_SUBTRACT,
KINC_G5_BLENDOP_MIN,
KINC_G5_BLENDOP_MAX
} kinc_g5_blending_operation_t;
typedef enum kinc_g5_cull_mode { KINC_G5_CULL_MODE_CLOCKWISE, KINC_G5_CULL_MODE_COUNTERCLOCKWISE, KINC_G5_CULL_MODE_NEVER } kinc_g5_cull_mode_t;
typedef enum kinc_g5_compare_mode {
KINC_G5_COMPARE_MODE_ALWAYS,
KINC_G5_COMPARE_MODE_NEVER,
KINC_G5_COMPARE_MODE_EQUAL,
KINC_G5_COMPARE_MODE_NOT_EQUAL,
KINC_G5_COMPARE_MODE_LESS,
KINC_G5_COMPARE_MODE_LESS_EQUAL,
KINC_G5_COMPARE_MODE_GREATER,
KINC_G5_COMPARE_MODE_GREATER_EQUAL
} kinc_g5_compare_mode_t;
typedef enum kinc_g5_stencil_action {
KINC_G5_STENCIL_ACTION_KEEP,
KINC_G5_STENCIL_ACTION_ZERO,
KINC_G5_STENCIL_ACTION_REPLACE,
KINC_G5_STENCIL_ACTION_INCREMENT,
KINC_G5_STENCIL_ACTION_INCREMENT_WRAP,
KINC_G5_STENCIL_ACTION_DECREMENT,
KINC_G5_STENCIL_ACTION_DECREMENT_WRAP,
KINC_G5_STENCIL_ACTION_INVERT
} kinc_g5_stencil_action_t;
typedef struct kinc_g5_pipeline {
kinc_g5_vertex_structure_t *inputLayout[16];
struct kinc_g5_shader *vertexShader;
struct kinc_g5_shader *fragmentShader;
struct kinc_g5_shader *geometryShader;
struct kinc_g5_shader *tessellationControlShader;
struct kinc_g5_shader *tessellationEvaluationShader;
kinc_g5_cull_mode_t cullMode;
bool depthWrite;
kinc_g5_compare_mode_t depthMode;
kinc_g5_compare_mode_t stencilMode;
kinc_g5_stencil_action_t stencilBothPass;
kinc_g5_stencil_action_t stencilDepthFail;
kinc_g5_stencil_action_t stencilFail;
int stencilReferenceValue;
int stencilReadMask;
int stencilWriteMask;
// One, Zero deactivates blending
kinc_g5_blending_factor_t blend_source;
kinc_g5_blending_factor_t blend_destination;
kinc_g5_blending_operation_t blend_operation;
kinc_g5_blending_factor_t alpha_blend_source;
kinc_g5_blending_factor_t alpha_blend_destination;
kinc_g5_blending_operation_t alpha_blend_operation;
bool colorWriteMaskRed[8]; // Per render target
bool colorWriteMaskGreen[8];
bool colorWriteMaskBlue[8];
bool colorWriteMaskAlpha[8];
int colorAttachmentCount;
kinc_g5_render_target_format_t colorAttachment[8];
int depthAttachmentBits;
int stencilAttachmentBits;
bool conservativeRasterization;
PipelineState5Impl impl;
} kinc_g5_pipeline_t;
typedef struct kinc_g5_compute_pipeline {
struct kinc_g5_shader *compute_shader;
ComputePipelineState5Impl impl;
} kinc_g5_compute_pipeline_t;
/// <summary>
/// Initializes a pipeline.
/// </summary>
/// <param name="state">The pipeline to initialize</param>
KINC_FUNC void kinc_g5_pipeline_init(kinc_g5_pipeline_t *pipeline);
void kinc_g5_internal_pipeline_init(kinc_g5_pipeline_t *pipeline);
/// <summary>
/// Destroys a pipeline.
/// </summary>
/// <param name="pipeline">The pipeline to destroy</param>
KINC_FUNC void kinc_g5_pipeline_destroy(kinc_g5_pipeline_t *pipeline);
/// <summary>
/// Compiles a pipeline. After a pipeline has been compiled it is finalized. It cannot be compiled again and further changes to the pipeline are ignored.
/// </summary>
/// <param name="pipeline">The pipeline to compile</param>
KINC_FUNC void kinc_g5_pipeline_compile(kinc_g5_pipeline_t *pipeline);
#ifndef KINC_KONG
/// <summary>
/// Searches for a constant/uniform and returns a constant-location which can be used to change the constant/uniform.
/// </summary>
/// <param name="pipeline">The pipeline to search in</param>
/// <param name="name">The name of the constant/uniform to find</param>
/// <returns>The constant-location of the constant/uniform</returns>
KINC_FUNC kinc_g5_constant_location_t kinc_g5_pipeline_get_constant_location(kinc_g5_pipeline_t *pipeline, const char *name);
/// <summary>
/// Searches for a texture-declaration and returns a texture-unit which can be used to assign a texture.
/// </summary>
/// <param name="pipeline">The pipeline to search in</param>
/// <param name="name">The name of the texture-declaration to search for</param>
/// <returns>The texture-unit of the texture-declaration</returns>
KINC_FUNC kinc_g5_texture_unit_t kinc_g5_pipeline_get_texture_unit(kinc_g5_pipeline_t *pipeline, const char *name);
#endif
/// <summary>
/// Initializes a compute-pipeline.
/// </summary>
/// <param name="state">The pipeline to initialize</param>
KINC_FUNC void kinc_g5_compute_pipeline_init(kinc_g5_compute_pipeline_t *pipeline);
void kinc_g5_internal_compute_pipeline_init(kinc_g5_compute_pipeline_t *pipeline);
/// <summary>
/// Destroys a compute-pipeline.
/// </summary>
/// <param name="pipeline">The pipeline to destroy</param>
KINC_FUNC void kinc_g5_compute_pipeline_destroy(kinc_g5_compute_pipeline_t *pipeline);
/// <summary>
/// Compiles a compute-pipeline. After a pipeline has been compiled it is finalized. It cannot be compiled again and further changes to the pipeline are
/// ignored.
/// </summary>
/// <param name="pipeline">The pipeline to compile</param>
KINC_FUNC void kinc_g5_compute_pipeline_compile(kinc_g5_compute_pipeline_t *pipeline);
/// <summary>
/// Searches for a constant/uniform and returns a constant-location which can be used to change the constant/uniform.
/// </summary>
/// <param name="pipeline">The pipeline to search in</param>
/// <param name="name">The name of the constant/uniform to find</param>
/// <returns>The constant-location of the constant/uniform</returns>
KINC_FUNC kinc_g5_constant_location_t kinc_g5_compute_pipeline_get_constant_location(kinc_g5_compute_pipeline_t *pipeline, const char *name);
/// <summary>
/// Searches for a texture-declaration and returns a texture-unit which can be used to assign a texture.
/// </summary>
/// <param name="pipeline">The pipeline to search in</param>
/// <param name="name">The name of the texture-declaration to search for</param>
/// <returns>The texture-unit of the texture-declaration</returns>
KINC_FUNC kinc_g5_texture_unit_t kinc_g5_compute_pipeline_get_texture_unit(kinc_g5_compute_pipeline_t *pipeline, const char *name);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,49 @@
#pragma once
/*! \file raytrace.h
\brief Preliminary API, requires some actual D3D12/Vulkan code to fill in
the acceleration-structure and pipeline-details. Also requires manually
compiled shaders. Use with caution.
*/
#include <kinc/global.h>
#include <kinc/backend/graphics5/raytrace.h>
#ifdef __cplusplus
extern "C" {
#endif
struct kinc_g5_command_list;
struct kinc_g5_constant_buffer;
struct kinc_g5_index_buffer;
struct kinc_g5_render_target;
struct kinc_g5_texture;
struct kinc_g5_vertex_buffer;
typedef struct kinc_raytrace_pipeline {
struct kinc_g5_constant_buffer *_constant_buffer;
kinc_raytrace_pipeline_impl_t impl;
} kinc_raytrace_pipeline_t;
KINC_FUNC void kinc_raytrace_pipeline_init(kinc_raytrace_pipeline_t *pipeline, struct kinc_g5_command_list *command_list, void *ray_shader, int ray_shader_size,
struct kinc_g5_constant_buffer *constant_buffer);
KINC_FUNC void kinc_raytrace_pipeline_destroy(kinc_raytrace_pipeline_t *pipeline);
typedef struct kinc_raytrace_acceleration_structure {
kinc_raytrace_acceleration_structure_impl_t impl;
} kinc_raytrace_acceleration_structure_t;
KINC_FUNC void kinc_raytrace_acceleration_structure_init(kinc_raytrace_acceleration_structure_t *accel, struct kinc_g5_command_list *command_list,
struct kinc_g5_vertex_buffer *vb, struct kinc_g5_index_buffer *ib);
KINC_FUNC void kinc_raytrace_acceleration_structure_destroy(kinc_raytrace_acceleration_structure_t *accel);
KINC_FUNC void kinc_raytrace_set_acceleration_structure(kinc_raytrace_acceleration_structure_t *accel);
KINC_FUNC void kinc_raytrace_set_pipeline(kinc_raytrace_pipeline_t *pipeline);
KINC_FUNC void kinc_raytrace_set_target(struct kinc_g5_texture *output);
KINC_FUNC void kinc_raytrace_dispatch_rays(struct kinc_g5_command_list *command_list);
KINC_FUNC void kinc_raytrace_copy(struct kinc_g5_command_list *command_list, struct kinc_g5_render_target *target, struct kinc_g5_texture *source);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,16 @@
#include "rendertarget.h"
void kinc_g5_render_target_init(kinc_g5_render_target_t *render_target, int width, int height, kinc_g5_render_target_format_t format, int depthBufferBits,
int stencilBufferBits) {
kinc_g5_render_target_init_with_multisampling(render_target, width, height, format, depthBufferBits, stencilBufferBits, 1);
}
void kinc_g5_render_target_init_framebuffer(kinc_g5_render_target_t *render_target, int width, int height, kinc_g5_render_target_format_t format,
int depthBufferBits, int stencilBufferBits) {
kinc_g5_render_target_init_framebuffer_with_multisampling(render_target, width, height, format, depthBufferBits, stencilBufferBits, 1);
}
void kinc_g5_render_target_init_cube(kinc_g5_render_target_t *render_target, int cubeMapSize, kinc_g5_render_target_format_t format, int depthBufferBits,
int stencilBufferBits) {
kinc_g5_render_target_init_cube_with_multisampling(render_target, cubeMapSize, format, depthBufferBits, stencilBufferBits, 1);
}

View File

@ -0,0 +1,134 @@
#pragma once
#include <kinc/global.h>
#include "textureunit.h"
#include <kinc/backend/graphics5/rendertarget.h>
/*! \file rendertarget.h
\brief Provides functions for handling render-targets.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef enum kinc_g5_render_target_format {
KINC_G5_RENDER_TARGET_FORMAT_32BIT,
KINC_G5_RENDER_TARGET_FORMAT_64BIT_FLOAT,
KINC_G5_RENDER_TARGET_FORMAT_32BIT_RED_FLOAT,
KINC_G5_RENDER_TARGET_FORMAT_128BIT_FLOAT,
KINC_G5_RENDER_TARGET_FORMAT_16BIT_DEPTH,
KINC_G5_RENDER_TARGET_FORMAT_8BIT_RED,
KINC_G5_RENDER_TARGET_FORMAT_16BIT_RED_FLOAT
} kinc_g5_render_target_format_t;
typedef struct kinc_g5_render_target {
int width;
int height;
int texWidth;
int texHeight;
int framebuffer_index;
bool isCubeMap;
bool isDepthAttachment;
RenderTarget5Impl impl;
} kinc_g5_render_target_t;
/// <summary>
/// Allocates and initializes a regular render-target.
/// </summary>
/// <param name="target"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="format"></param>
/// <param name="depthBufferBits"></param>
/// <param name="stencilBufferBits"></param>
/// <returns></returns>
KINC_FUNC void kinc_g5_render_target_init(kinc_g5_render_target_t *target, int width, int height, kinc_g5_render_target_format_t format, int depthBufferBits,
int stencilBufferBits);
/// <summary>
/// Allocates and initializes a regular render-target. Can fall back to a regular render-target.
/// </summary>
/// <param name="target"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="format"></param>
/// <param name="depthBufferBits"></param>
/// <param name="stencilBufferBits"></param>
/// <param name="samples_per_pixel"></param>
/// <returns></returns>
KINC_FUNC void kinc_g5_render_target_init_with_multisampling(kinc_g5_render_target_t *target, int width, int height, kinc_g5_render_target_format_t format,
int depthBufferBits, int stencilBufferBits, int samples_per_pixel);
/// <summary>
/// Allocates and initializes a framebuffer.
/// </summary>
/// <param name="target"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="format"></param>
/// <param name="depthBufferBits"></param>
/// <param name="stencilBufferBits"></param>
/// <returns></returns>
KINC_FUNC void kinc_g5_render_target_init_framebuffer(kinc_g5_render_target_t *target, int width, int height, kinc_g5_render_target_format_t format,
int depthBufferBits, int stencilBufferBits);
/// <summary>
/// Allocates and initializes a multisampled framebuffer. Can fall back to a regular framebuffer.
/// </summary>
/// <param name="target"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="format"></param>
/// <param name="depthBufferBits"></param>
/// <param name="stencilBufferBits"></param>
/// <param name="samples_per_pixel"></param>
/// <returns></returns>
KINC_FUNC void kinc_g5_render_target_init_framebuffer_with_multisampling(kinc_g5_render_target_t *target, int width, int height,
kinc_g5_render_target_format_t format, int depthBufferBits, int stencilBufferBits,
int samples_per_pixel);
/// <summary>
/// Allocates and initializes a render-target-cube-map.
/// </summary>
/// <param name="target"></param>
/// <param name="cubeMapSize"></param>
/// <param name="format"></param>
/// <param name="depthBufferBits"></param>
/// <param name="stencilBufferBits"></param>
/// <returns></returns>
KINC_FUNC void kinc_g5_render_target_init_cube(kinc_g5_render_target_t *target, int cubeMapSize, kinc_g5_render_target_format_t format, int depthBufferBits,
int stencilBufferBits);
/// <summary>
/// Allocates and initializes a multisampled render-target-cube-map. Can fall back to a regular cube-map.
/// </summary>
/// <param name="target"></param>
/// <param name="cubeMapSize"></param>
/// <param name="format"></param>
/// <param name="depthBufferBits"></param>
/// <param name="stencilBufferBits"></param>
/// <param name="samples_per_pixel"></param>
/// <returns></returns>
KINC_FUNC void kinc_g5_render_target_init_cube_with_multisampling(kinc_g5_render_target_t *target, int cubeMapSize, kinc_g5_render_target_format_t format,
int depthBufferBits, int stencilBufferBits, int samples_per_pixel);
/// <summary>
/// Deallocates and destroys a render-target.
/// </summary>
/// <param name="renderTarget">The render-target to destroy</param>
KINC_FUNC void kinc_g5_render_target_destroy(kinc_g5_render_target_t *target);
/// <summary>
/// Copies the depth and stencil-components of one render-target into another one.
/// </summary>
/// <param name="renderTarget">The render-target to copy the data into</param>
/// <param name="source">The render-target from which to copy the data</param>
/// <returns></returns>
KINC_FUNC void kinc_g5_render_target_set_depth_stencil_from(kinc_g5_render_target_t *target, kinc_g5_render_target_t *source);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,19 @@
#include "sampler.h"
void kinc_g5_sampler_options_set_defaults(kinc_g5_sampler_options_t *options) {
options->u_addressing = KINC_G5_TEXTURE_ADDRESSING_CLAMP;
options->v_addressing = KINC_G5_TEXTURE_ADDRESSING_CLAMP;
options->w_addressing = KINC_G5_TEXTURE_ADDRESSING_CLAMP;
options->magnification_filter = KINC_G5_TEXTURE_FILTER_POINT;
options->minification_filter = KINC_G5_TEXTURE_FILTER_POINT;
options->mipmap_filter = KINC_G5_MIPMAP_FILTER_POINT;
options->lod_min_clamp = 0.0f;
options->lod_max_clamp = 32.0f;
options->is_comparison = false;
options->compare_mode = KINC_G5_COMPARE_MODE_ALWAYS;
options->max_anisotropy = 1;
}

View File

@ -0,0 +1,78 @@
#pragma once
#include <kinc/backend/graphics5/sampler.h>
#include <kinc/graphics5/pipeline.h>
/*! \file sampler.h
\brief Provides functions for sampler objects.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef enum kinc_g5_texture_addressing {
KINC_G5_TEXTURE_ADDRESSING_REPEAT,
KINC_G5_TEXTURE_ADDRESSING_MIRROR,
KINC_G5_TEXTURE_ADDRESSING_CLAMP,
KINC_G5_TEXTURE_ADDRESSING_BORDER
} kinc_g5_texture_addressing_t;
typedef enum kinc_g5_texture_filter {
KINC_G5_TEXTURE_FILTER_POINT,
KINC_G5_TEXTURE_FILTER_LINEAR,
KINC_G5_TEXTURE_FILTER_ANISOTROPIC
} kinc_g5_texture_filter_t;
typedef enum kinc_g5_mipmap_filter {
KINC_G5_MIPMAP_FILTER_NONE,
KINC_G5_MIPMAP_FILTER_POINT,
KINC_G5_MIPMAP_FILTER_LINEAR // linear texture filter + linear mip filter -> trilinear filter
} kinc_g5_mipmap_filter_t;
typedef struct kinc_g5_sampler_options {
kinc_g5_texture_addressing_t u_addressing;
kinc_g5_texture_addressing_t v_addressing;
kinc_g5_texture_addressing_t w_addressing;
kinc_g5_texture_filter_t minification_filter;
kinc_g5_texture_filter_t magnification_filter;
kinc_g5_mipmap_filter_t mipmap_filter;
float lod_min_clamp;
float lod_max_clamp;
uint16_t max_anisotropy;
bool is_comparison;
kinc_g5_compare_mode_t compare_mode;
} kinc_g5_sampler_options_t;
typedef struct kinc_g5_sampler {
kinc_g5_sampler_impl_t impl;
} kinc_g5_sampler_t;
/// <summary>
/// Initializes the passed options-object with the default-options.
/// </summary>
/// <param name="options">The options-object for which the default-options will be set</param>
KINC_FUNC void kinc_g5_sampler_options_set_defaults(kinc_g5_sampler_options_t *options);
/// <summary>
/// Creates a sampler-object.
///
/// On platforms such as older OpenGL not all sampler attributes may be available.
/// </summary>
/// <param name="sampler">Pointer to the sampler object to initialize</param>
/// <param name="descriptor">Options for the sampler</param>
KINC_FUNC void kinc_g5_sampler_init(kinc_g5_sampler_t *sampler, const kinc_g5_sampler_options_t *options);
/// <summary>
/// Destroys a sampler-object.
/// </summary>
/// <param name="sampler">The sampler-object to destroy</param>
KINC_FUNC void kinc_g5_sampler_destroy(kinc_g5_sampler_t *sampler);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,50 @@
#pragma once
#include <kinc/global.h>
#include <kinc/backend/graphics5/shader.h>
#include <stddef.h>
/*! \file shader.h
\brief Provides functions for creating and destroying shaders.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef enum kinc_g5_shader_type {
KINC_G5_SHADER_TYPE_FRAGMENT,
KINC_G5_SHADER_TYPE_VERTEX,
KINC_G5_SHADER_TYPE_COMPUTE,
KINC_G5_SHADER_TYPE_GEOMETRY,
KINC_G5_SHADER_TYPE_TESSELLATION_CONTROL,
KINC_G5_SHADER_TYPE_TESSELLATION_EVALUATION,
KINC_G5_SHADER_TYPE_COUNT
} kinc_g5_shader_type_t;
typedef struct kinc_g5_shader {
Shader5Impl impl;
} kinc_g5_shader_t;
/// <summary>
/// Initializes a shader based on system-specific shader-data. The system-specific shader-data is usually created per system by the krafix-shader-compiler which
/// is automatically called by kincmake.
/// </summary>
/// <param name="shader">The shader to initialize</param>
/// <param name="data">The system-specific shader-data</param>
/// <param name="length">The length of the system-specific shader-data in bytes</param>
/// <param name="type">The type of the shader</param>
KINC_FUNC void kinc_g5_shader_init(kinc_g5_shader_t *shader, const void *source, size_t length, kinc_g5_shader_type_t type);
/// <summary>
/// Destroys a shader.
/// </summary>
/// <param name="shader">The shader to destroy</param>
KINC_FUNC void kinc_g5_shader_destroy(kinc_g5_shader_t *shader);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,32 @@
#include "texture.h"
/*
Graphics5::Texture::Texture(Kore::Reader& reader, const char* format, bool readable) : Image(reader, format, readable) {
_init(format, readable);
}
Graphics5::Texture::Texture(const char* filename, bool readable) {
FileReader reader(filename);
Image::init(reader, filename, readable);
_init(filename, readable);
}
Graphics5::Texture::Texture(void* data, int size, const char* format, bool readable) {
BufferReader reader(data, size);
Image::init(reader, format, readable);
_init(format, readable);
}
Graphics5::Texture::Texture(void* data, int width, int height, int format, bool readable) : Image(data, width, height, Image::Format(format), readable) {
_init("", readable);
}
*/
bool kinc_g5_texture_unit_equals(kinc_g5_texture_unit_t *unit1, kinc_g5_texture_unit_t *unit2) {
for (int i = 0; i < KINC_G5_SHADER_TYPE_COUNT; ++i) {
if (unit1->stages[i] != unit2->stages[i]) {
return false;
}
}
return true;
}

View File

@ -0,0 +1,98 @@
#pragma once
#include <kinc/global.h>
#include <kinc/image.h>
#include "textureunit.h"
#include <kinc/backend/graphics5/texture.h>
/*! \file texture.h
\brief Provides functions for handling textures.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef struct kinc_g5_texture {
int texWidth;
int texHeight;
kinc_image_format_t format;
Texture5Impl impl;
} kinc_g5_texture_t;
/// <summary>
/// Allocates and initializes a texture without copying any data into it.
/// </summary>
/// <param name="texture">The texture to initialize</param>
/// <param name="width">The width of the texture to create</param>
/// <param name="height">The height of the texture to create</param>
/// <param name="format">The format of the texture to create</param>
KINC_FUNC void kinc_g5_texture_init(kinc_g5_texture_t *texture, int width, int height, kinc_image_format_t format);
/// <summary>
/// Allocates and initializes a 3d-texture without copying any data into it.
/// </summary>
/// <param name="texture">The texture to initialize</param>
/// <param name="width">The width of the texture to create</param>
/// <param name="height">The height of the texture to create</param>
/// <param name="depth">The depth of the texture to create</param>
/// <param name="format">The format of the texture to create</param>
KINC_FUNC void kinc_g5_texture_init3d(kinc_g5_texture_t *texture, int width, int height, int depth, kinc_image_format_t format);
/// <summary>
/// Allocates and initializes a texture and copies image-data into it.
/// </summary>
/// <param name="texture">The texture to initialize</param>
/// <param name="image">The image which's data is copied into the texture</param>
KINC_FUNC void kinc_g5_texture_init_from_image(kinc_g5_texture_t *texture, kinc_image_t *image);
// void kinc_g5_texture_init_from_encoded_data(kinc_g5_texture_t *texture, void *data, int size, const char *format, bool readable);
// void kinc_g5_texture_init_from_data(kinc_g5_texture_t *texture, void *data, int width, int height, int format, bool readable);
KINC_FUNC void kinc_g5_texture_init_non_sampled_access(kinc_g5_texture_t *texture, int width, int height, kinc_image_format_t format);
/// <summary>
/// Deallocates and destroys a texture.
/// </summary>
/// <param name="texture">The texture to destroy</param>
KINC_FUNC void kinc_g5_texture_destroy(kinc_g5_texture_t *texture);
#ifdef KORE_ANDROID
KINC_FUNC void kinc_g5_texture_init_from_id(kinc_g5_texture_t *texture, unsigned texid);
#endif
KINC_FUNC uint8_t *kinc_g5_texture_lock(kinc_g5_texture_t *texture);
KINC_FUNC void kinc_g5_texture_unlock(kinc_g5_texture_t *texture);
/// <summary>
/// Clears parts of a texture to a color.
/// </summary>
KINC_FUNC void kinc_g5_texture_clear(kinc_g5_texture_t *texture, int x, int y, int z, int width, int height, int depth, unsigned color);
#if defined(KORE_IOS) || defined(KORE_MACOS)
KINC_FUNC void kinc_g5_texture_upload(kinc_g5_texture_t *texture, uint8_t *data);
#endif
/// <summary>
/// Generates the mipmap-chain for a texture.
/// </summary>
/// <param name="renderTarget">The render-target to create the mipmaps for</param>
/// <param name="levels">The number of mipmap-levels to generate</param>
KINC_FUNC void kinc_g5_texture_generate_mipmaps(kinc_g5_texture_t *texture, int levels);
KINC_FUNC void kinc_g5_texture_set_mipmap(kinc_g5_texture_t *texture, kinc_image_t *mipmap, int level);
/// <summary>
/// Returns the stride of the first mipmap-layer of the texture in bytes.
/// </summary>
/// <param name="texture">The texture to figure out the stride for</param>
/// <returns>The stride of the first mipmap-layer in bytes</returns>
KINC_FUNC int kinc_g5_texture_stride(kinc_g5_texture_t *texture);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,24 @@
#pragma once
#include <kinc/global.h>
#include <kinc/backend/graphics5/texture.h>
#include <kinc/graphics5/shader.h>
/*! \file textureunit.h
\brief Provides a texture-unit-struct which is used for setting textures in a shader.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef struct kinc_g5_texture_unit {
int stages[KINC_G5_SHADER_TYPE_COUNT];
} kinc_g5_texture_unit_t;
bool kinc_g5_texture_unit_equals(kinc_g5_texture_unit_t *unit1, kinc_g5_texture_unit_t *unit2);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,85 @@
#pragma once
#include <kinc/global.h>
#include "vertexstructure.h"
#include <kinc/backend/graphics5/vertexbuffer.h>
/*! \file vertexbuffer.h
\brief Provides functions for setting up and using vertex-buffers.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef struct kinc_g5_vertex_buffer {
VertexBuffer5Impl impl;
} kinc_g5_vertex_buffer_t;
/// <summary>
/// Allocate and initialize a vertex-buffer.
/// </summary>
/// <param name="buffer">The buffer to initialize</param>
/// <param name="count">The number of vertices in the buffer</param>
/// <param name="structure">The structure of the buffer</param>
/// <param name="gpu_memory">If true, the vertex-buffer will reside in gpu-memory which will make it slower to update but faster to use</param>
/// <param name="instance_data_step_rate">The step-rate for instanced-rendering - use 0 if instanced-rendering will not be used with this buffer</param>
KINC_FUNC void kinc_g5_vertex_buffer_init(kinc_g5_vertex_buffer_t *buffer, int count, kinc_g5_vertex_structure_t *structure, bool gpu_memory,
int instance_data_step_rate);
/// <summary>
/// Destroys a vertex-buffer.
/// </summary>
/// <param name="buffer">The buffer to destroy</param>
KINC_FUNC void kinc_g5_vertex_buffer_destroy(kinc_g5_vertex_buffer_t *buffer);
/// <summary>
/// Locks all of a vertex-buffer to modify its contents.
/// </summary>
/// <param name="buffer">The buffer to lock</param>
/// <returns>The contents of the buffer</returns>
KINC_FUNC float *kinc_g5_vertex_buffer_lock_all(kinc_g5_vertex_buffer_t *buffer);
/// <summary>
/// Locks part of a vertex-buffer to modify its contents.
/// </summary>
/// <param name="buffer">The buffer to lock</param>
/// <param name="start">The index of the first vertex to lock</param>
/// <param name="count">The number of vertices to lock</param>
/// <returns>The contents of the buffer, starting at start</returns>
KINC_FUNC float *kinc_g5_vertex_buffer_lock(kinc_g5_vertex_buffer_t *buffer, int start, int count);
/// <summary>
/// Unlock all of a vertex-buffer so the changed contents can be used.
/// </summary>
/// <param name="buffer">The buffer to unlock</param>
KINC_FUNC void kinc_g5_vertex_buffer_unlock_all(kinc_g5_vertex_buffer_t *buffer);
/// <summary>
/// Unlocks part of a vertex-buffer so the changed contents can be used.
/// </summary>
/// <param name="buffer">The buffer to unlock</param>
/// <param name="count">The number of vertices to unlock, starting from the start-vertex from the previous lock-call</param>
KINC_FUNC void kinc_g5_vertex_buffer_unlock(kinc_g5_vertex_buffer_t *buffer, int count);
/// <summary>
/// Returns the number of vertices in a buffer.
/// </summary>
/// <param name="buffer">The buffer to figure out the number of vertices for</param>
/// <returns>The number of vertices</returns>
KINC_FUNC int kinc_g5_vertex_buffer_count(kinc_g5_vertex_buffer_t *buffer);
/// <summary>
/// Returns the stride aka the size of one vertex of the buffer in bytes.
/// </summary>
/// <param name="buffer">The buffer to figure out the stride for</param>
/// <returns>The stride of the buffer in bytes</returns>
KINC_FUNC int kinc_g5_vertex_buffer_stride(kinc_g5_vertex_buffer_t *buffer);
int kinc_g5_internal_vertex_buffer_set(kinc_g5_vertex_buffer_t *buffer, int offset);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,45 @@
#pragma once
#include <kinc/global.h>
#include <kinc/graphics4/vertexstructure.h>
/*! \file vertexstructure.h
\brief Provides types for setting up the structure of vertices in a vertex-buffer.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef kinc_g4_vertex_data_t kinc_g5_vertex_data_t;
// typedef kinc_g4_vertex_attribute_t kinc_g5_vertex_attribute_t;
typedef kinc_g4_vertex_element_t kinc_g5_vertex_element_t;
typedef kinc_g4_vertex_structure_t kinc_g5_vertex_structure_t;
/// <summary>
/// Initializes a vertex-structure.
/// </summary>
/// <param name="structure">The structure to initialize</param>
/// <returns></returns>
static inline void kinc_g5_vertex_structure_init(kinc_g5_vertex_structure_t *structure) {
kinc_g4_vertex_structure_init(structure);
}
/// <summary>
/// Adds an element to a vertex-structure.
/// </summary>
/// <param name="structure">The structure to add an element to</param>
/// <param name="name">The name to use for the new element</param>
/// <param name="data">The type of data to assign for the new element</param>
/// <returns></returns>
static inline void kinc_g5_vertex_structure_add(kinc_g5_vertex_structure_t *structure, const char *name, kinc_g5_vertex_data_t data) {
kinc_g4_vertex_structure_add(structure, name, data);
}
#ifdef __cplusplus
}
#endif

754
Kinc/Sources/kinc/image.h Normal file
View File

@ -0,0 +1,754 @@
#pragma once
#include <kinc/global.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
/*! \file image.h
\brief Functionality for creating and loading images. Image loading supports PNG, JPEG and the custom K format. K files can contain image data that uses
texture-compression (see kinc_image_compression).
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef enum kinc_image_compression {
KINC_IMAGE_COMPRESSION_NONE,
KINC_IMAGE_COMPRESSION_DXT5,
KINC_IMAGE_COMPRESSION_ASTC,
KINC_IMAGE_COMPRESSION_PVRTC
} kinc_image_compression_t;
typedef enum kinc_image_format {
KINC_IMAGE_FORMAT_RGBA32,
KINC_IMAGE_FORMAT_GREY8,
KINC_IMAGE_FORMAT_RGB24,
KINC_IMAGE_FORMAT_RGBA128,
KINC_IMAGE_FORMAT_RGBA64,
KINC_IMAGE_FORMAT_A32,
KINC_IMAGE_FORMAT_BGRA32,
KINC_IMAGE_FORMAT_A16
} kinc_image_format_t;
typedef struct kinc_image {
int width, height, depth;
kinc_image_format_t format;
unsigned internal_format;
kinc_image_compression_t compression;
void *data;
int data_size;
} kinc_image_t;
typedef struct kinc_image_read_callbacks {
int (*read)(void *user_data, void *data, size_t size);
void (*seek)(void *user_data, int pos);
int (*pos)(void *user_data);
size_t (*size)(void *user_data);
} kinc_image_read_callbacks_t;
/// <summary>
/// Creates a 2D kinc_image in the provided memory.
/// </summary>
/// <returns>The size that's occupied by the image in memory in bytes</returns>
KINC_FUNC size_t kinc_image_init(kinc_image_t *image, void *memory, int width, int height, kinc_image_format_t format);
/// <summary>
/// Creates a 3D kinc_image in the provided memory.
/// </summary>
/// <returns>The size that's occupied by the image in memory in bytes</returns>
KINC_FUNC size_t kinc_image_init3d(kinc_image_t *image, void *memory, int width, int height, int depth, kinc_image_format_t format);
/// <summary>
/// Peeks into an image file and figures out the size it will occupy in memory.
/// </summary>
/// <returns>The memory size in bytes that will be used when loading the image</returns>
KINC_FUNC size_t kinc_image_size_from_file(const char *filename);
/// <summary>
/// Peeks into an image that is loaded via callback functions and figures out the size it will occupy in memory.
/// </summary>
/// <returns>The memory size in bytes that will be used when loading the image</returns>
KINC_FUNC size_t kinc_image_size_from_callbacks(kinc_image_read_callbacks_t callbacks, void *user_data, const char *format);
/// <summary>
/// Peeks into an image file that resides in memory and figures out the size it will occupy in memory once it is uncompressed.
/// </summary>
/// <param name="data">The encoded data</param>
/// <param name="data_size">The size of the encoded data</param>
/// <param name="format_hint">Something like "png" can help, it also works to just put in the filename</param>
/// <returns>The memory size in bytes that will be used when loading the image</returns>
KINC_FUNC size_t kinc_image_size_from_encoded_bytes(void *data, size_t data_size, const char *format_hint);
/// <summary>
/// Loads an image from a file.
/// </summary>
/// <returns>The memory size in bytes that will be used when loading the image</returns>
KINC_FUNC size_t kinc_image_init_from_file(kinc_image_t *image, void *memory, const char *filename);
/// <summary>
/// Loads an image file using callbacks.
/// </summary>
/// <returns>The memory size in bytes that will be used when loading the image</returns>
KINC_FUNC size_t kinc_image_init_from_callbacks(kinc_image_t *image, void *memory, kinc_image_read_callbacks_t callbacks, void *user_data, const char *format);
/// <summary>
/// Loads an image file from a memory.
/// </summary>
/// <returns>The memory size in bytes that will be used when loading the image</returns>
KINC_FUNC size_t kinc_image_init_from_encoded_bytes(kinc_image_t *image, void *memory, void *data, size_t data_size, const char *format);
/// <summary>
/// Creates a 2D image from memory.
/// </summary>
KINC_FUNC void kinc_image_init_from_bytes(kinc_image_t *image, void *data, int width, int height, kinc_image_format_t format);
/// <summary>
/// Creates a 3D image from memory.
/// </summary>
KINC_FUNC void kinc_image_init_from_bytes3d(kinc_image_t *image, void *data, int width, int height, int depth, kinc_image_format_t format);
/// <summary>
/// Destroys an image. This does not free the user-provided memory.
/// </summary>
KINC_FUNC void kinc_image_destroy(kinc_image_t *image);
/// <summary>
/// Gets the color value of a 32 bit pixel. If this doesn't fit the format of the image please use kinc_image_at_raw instead.
/// </summary>
/// <returns>One 32 bit color value</returns>
KINC_FUNC uint32_t kinc_image_at(kinc_image_t *image, int x, int y);
/// <summary>
/// Gets a pointer to the color-data of one pixel.
/// </summary>
/// <returns>A pointer to the color-data of the pixel pointed to by x and y</returns>
KINC_FUNC void *kinc_image_at_raw(kinc_image_t *image, int x, int y);
/// <summary>
/// Provides access to the image data.
/// </summary>
/// <returns>A pointer to the image data</returns>
KINC_FUNC uint8_t *kinc_image_get_pixels(kinc_image_t *image);
/// <summary>
/// Gets the size in bytes of a single pixel for a given image format.
/// </summary>
/// <returns>The size of one pixel in bytes</returns>
KINC_FUNC int kinc_image_format_sizeof(kinc_image_format_t format);
#ifdef KINC_IMPLEMENTATION_ROOT
#define KINC_IMPLEMENTATION
#endif
#ifdef KINC_IMPLEMENTATION
#include "image.h"
#ifdef KORE_LZ4X
#include <kinc/libs/lz4x.h>
#else
#include <kinc/io/lz4/lz4.h>
#endif
#ifdef KINC_IMPLEMENTATION_ROOT
#undef KINC_IMPLEMENTATION
#endif
#include <kinc/log.h>
#ifdef KINC_IMPLEMENTATION_ROOT
#define KINC_IMPLEMENTATION
#endif
#undef KINC_IMPLEMENTATION
#include <kinc/graphics4/graphics.h>
#include <kinc/io/filereader.h>
#include <kinc/math/core.h>
#define KINC_IMPLEMENTATION
#include <string.h>
#define BUFFER_SIZE 4096 * 4096 * 4
static uint8_t buffer[BUFFER_SIZE];
static size_t buffer_offset = 0;
static uint8_t *last_allocated_pointer = 0;
static void *buffer_malloc(size_t size) {
uint8_t *current = &buffer[buffer_offset];
buffer_offset += size + sizeof(size_t);
if (buffer_offset > BUFFER_SIZE) {
kinc_log(KINC_LOG_LEVEL_ERROR, "Not enough memory on image.c Buffer.");
return NULL;
}
*(size_t *)current = size;
last_allocated_pointer = current + sizeof(size_t);
return current + sizeof(size_t);
}
static void *buffer_realloc(void *p, size_t size) {
if (p == NULL) {
return buffer_malloc(size);
}
uint8_t *old_pointer = (uint8_t *)p;
size_t old_size = *(size_t *)(old_pointer - sizeof(size_t));
if (size <= old_size) {
return old_pointer;
}
else {
if (last_allocated_pointer == old_pointer) {
size_t last_size = &buffer[buffer_offset] - old_pointer;
size_t size_diff = size - last_size;
buffer_offset += size_diff + sizeof(size_t);
return old_pointer;
}
uint8_t *new_pointer = (uint8_t *)buffer_malloc(size);
if (new_pointer == NULL) {
return NULL;
}
memcpy(new_pointer, old_pointer, old_size < size ? old_size : size);
return new_pointer;
}
}
static void buffer_free(void *p) {}
#define STBI_MALLOC(sz) buffer_malloc(sz)
#define STBI_REALLOC(p, newsz) buffer_realloc(p, newsz)
#define STBI_FREE(p) buffer_free(p)
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_STATIC
#ifdef __clang__
#pragma clang diagnostic ignored "-Wunused-function"
#endif
#include <kinc/libs/stb_image.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
typedef struct {
kinc_image_read_callbacks_t callbacks;
void *user_data;
} read_data;
// fill 'data' with 'size' bytes. return number of bytes actually read
static int stb_read(void *user, char *data, int size) {
read_data *reader = (read_data *)user;
return reader->callbacks.read(reader->user_data, data, size);
}
// skip the next 'n' bytes, or 'unget' the last -n bytes if negative
static void stb_skip(void *user, int n) {
read_data *reader = (read_data *)user;
reader->callbacks.seek(reader->user_data, reader->callbacks.pos(reader->user_data) + n);
}
// returns nonzero if we are at end of file/data
static int stb_eof(void *user) {
read_data *reader = (read_data *)user;
return reader->callbacks.pos(reader->user_data) == reader->callbacks.size(reader->user_data);
}
static _Bool endsWith(const char *str, const char *suffix) {
if (str == NULL || suffix == NULL)
return 0;
size_t lenstr = strlen(str);
size_t lensuffix = strlen(suffix);
if (lensuffix > lenstr)
return 0;
return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0;
}
static size_t loadImageSize(kinc_image_read_callbacks_t callbacks, void *user_data, const char *filename) {
if (endsWith(filename, "k")) {
uint8_t data[4];
callbacks.read(user_data, data, 4);
int width = kinc_read_s32le(data);
callbacks.read(user_data, data, 4);
int height = kinc_read_s32le(data);
char fourcc[5];
callbacks.read(user_data, fourcc, 4);
fourcc[4] = 0;
if (strcmp(fourcc, "LZ4 ") == 0) {
return width * height * 4;
}
else if (strcmp(fourcc, "LZ4F") == 0) {
return width * height * 16;
}
else if (strcmp(fourcc, "ASTC") == 0) {
return width * height * 4; // just an upper bound
}
else if (strcmp(fourcc, "DXT5") == 0) {
return width * height; // just an upper bound
}
else {
kinc_log(KINC_LOG_LEVEL_ERROR, "Unknown fourcc in .k file.");
return 0;
}
}
else if (endsWith(filename, "pvr")) {
uint8_t data[4];
callbacks.read(user_data, data, 4); // version
callbacks.read(user_data, data, 4); // flags
callbacks.read(user_data, data, 4); // pixelFormat1
callbacks.read(user_data, data, 4); // colourSpace
callbacks.read(user_data, data, 4); // channelType
callbacks.read(user_data, data, 4);
uint32_t hh = kinc_read_u32le(data);
callbacks.read(user_data, data, 4);
uint32_t ww = kinc_read_u32le(data);
return ww * hh / 2;
}
else if (endsWith(filename, "hdr")) {
stbi_io_callbacks stbi_callbacks;
stbi_callbacks.eof = stb_eof;
stbi_callbacks.read = stb_read;
stbi_callbacks.skip = stb_skip;
read_data reader;
reader.callbacks = callbacks;
reader.user_data = user_data;
int x, y, comp;
stbi_info_from_callbacks(&stbi_callbacks, &reader, &x, &y, &comp);
buffer_offset = 0;
return x * y * 16;
}
else {
stbi_io_callbacks stbi_callbacks;
stbi_callbacks.eof = stb_eof;
stbi_callbacks.read = stb_read;
stbi_callbacks.skip = stb_skip;
read_data reader;
reader.callbacks = callbacks;
reader.user_data = user_data;
int x, y, comp;
stbi_info_from_callbacks(&stbi_callbacks, &reader, &x, &y, &comp);
buffer_offset = 0;
return x * y * 4;
}
}
static bool loadImage(kinc_image_read_callbacks_t callbacks, void *user_data, const char *filename, uint8_t *output, int *outputSize, int *width, int *height,
kinc_image_compression_t *compression, kinc_image_format_t *format, unsigned *internalFormat) {
*format = KINC_IMAGE_FORMAT_RGBA32;
if (endsWith(filename, "k")) {
uint8_t data[4];
callbacks.read(user_data, data, 4);
*width = kinc_read_s32le(data);
callbacks.read(user_data, data, 4);
*height = kinc_read_s32le(data);
char fourcc[5];
callbacks.read(user_data, fourcc, 4);
fourcc[4] = 0;
int compressedSize = (int)callbacks.size(user_data) - 12;
if (strcmp(fourcc, "LZ4 ") == 0) {
*compression = KINC_IMAGE_COMPRESSION_NONE;
*internalFormat = 0;
*outputSize = *width * *height * 4;
callbacks.read(user_data, buffer, compressedSize);
LZ4_decompress_safe((char *)buffer, (char *)output, compressedSize, *outputSize);
return true;
}
else if (strcmp(fourcc, "LZ4F") == 0) {
*compression = KINC_IMAGE_COMPRESSION_NONE;
*internalFormat = 0;
*outputSize = *width * *height * 16;
callbacks.read(user_data, buffer, compressedSize);
LZ4_decompress_safe((char *)buffer, (char *)output, compressedSize, *outputSize);
*format = KINC_IMAGE_FORMAT_RGBA128;
return true;
}
else if (strcmp(fourcc, "ASTC") == 0) {
*compression = KINC_IMAGE_COMPRESSION_ASTC;
*outputSize = *width * *height * 4;
callbacks.read(user_data, buffer, compressedSize);
*outputSize = LZ4_decompress_safe((char *)buffer, (char *)output, compressedSize, *outputSize);
uint8_t blockdim_x = 6;
uint8_t blockdim_y = 6;
*internalFormat = (blockdim_x << 8) + blockdim_y;
return true;
/*int index = 0;
index += 4; // magic
u8 blockdim_x = astcdata[index++];
u8 blockdim_y = astcdata[index++];
++index; // blockdim_z
internalFormat = (blockdim_x << 8) + blockdim_y;
u8 xsize[4];
xsize[0] = astcdata[index++];
xsize[1] = astcdata[index++];
xsize[2] = astcdata[index++];
xsize[3] = 0;
this->width = *(unsigned*)&xsize[0];
u8 ysize[4];
ysize[0] = astcdata[index++];
ysize[1] = astcdata[index++];
ysize[2] = astcdata[index++];
ysize[3] = 0;
this->height = *(unsigned*)&ysize[0];
u8 zsize[3];
zsize[0] = astcdata[index++];
zsize[1] = astcdata[index++];
zsize[2] = astcdata[index++];
u8* all = (u8*)astcdata[index];
dataSize -= 16;
this->data = new u8[dataSize];
for (int i = 0; i < dataSize; ++i) {
data[i] = all[16 + i];
}
free(astcdata);*/
}
else if (strcmp(fourcc, "DXT5") == 0) {
*compression = KINC_IMAGE_COMPRESSION_DXT5;
*outputSize = *width * *height;
callbacks.read(user_data, buffer, compressedSize);
*outputSize = LZ4_decompress_safe((char *)buffer, (char *)output, compressedSize, *outputSize);
*internalFormat = 0;
return true;
}
else {
kinc_log(KINC_LOG_LEVEL_ERROR, "Unknown fourcc in .k file.");
return false;
}
}
else if (endsWith(filename, "pvr")) {
uint8_t data[4];
callbacks.read(user_data, data, 4); // version
callbacks.read(user_data, data, 4); // flags
callbacks.read(user_data, data, 4); // pixelFormat1
callbacks.read(user_data, data, 4); // colourSpace
callbacks.read(user_data, data, 4); // channelType
callbacks.read(user_data, data, 4);
uint32_t hh = kinc_read_u32le(data);
callbacks.read(user_data, data, 4);
uint32_t ww = kinc_read_u32le(data);
callbacks.read(user_data, data, 4); // depth
callbacks.read(user_data, data, 4); // numSurfaces
callbacks.read(user_data, data, 4); // numFaces
callbacks.read(user_data, data, 4); // mipMapCount
callbacks.read(user_data, data, 4);
callbacks.read(user_data, data, 4);
uint32_t meta1fourcc = kinc_read_u32le(data);
callbacks.read(user_data, data, 4); // meta1key
callbacks.read(user_data, data, 4); // meta1size
callbacks.read(user_data, data, 4);
uint32_t meta1data = kinc_read_u32le(data);
callbacks.read(user_data, data, 4);
uint32_t meta2fourcc = kinc_read_u32le(data);
callbacks.read(user_data, data, 4); // meta2key
callbacks.read(user_data, data, 4); // meta2size
callbacks.read(user_data, data, 4);
uint32_t meta2data = kinc_read_u32le(data);
int w = 0;
int h = 0;
if (meta1fourcc == 0)
w = meta1data;
if (meta1fourcc == 1)
h = meta1data;
if (meta2fourcc == 0)
w = meta2data;
if (meta2fourcc == 1)
h = meta2data;
*width = w;
*height = h;
*compression = KINC_IMAGE_COMPRESSION_PVRTC;
*internalFormat = 0;
*outputSize = ww * hh / 2;
callbacks.read(user_data, output, *outputSize);
return true;
}
else if (endsWith(filename, "hdr")) {
*compression = KINC_IMAGE_COMPRESSION_NONE;
*internalFormat = 0;
stbi_io_callbacks stbi_callbacks;
stbi_callbacks.eof = stb_eof;
stbi_callbacks.read = stb_read;
stbi_callbacks.skip = stb_skip;
read_data reader;
reader.callbacks = callbacks;
reader.user_data = user_data;
int comp;
float *uncompressed = stbi_loadf_from_callbacks(&stbi_callbacks, &reader, width, height, &comp, 4);
if (uncompressed == NULL) {
kinc_log(KINC_LOG_LEVEL_ERROR, stbi_failure_reason());
return false;
}
*outputSize = *width * *height * 16;
memcpy(output, uncompressed, *outputSize);
*format = KINC_IMAGE_FORMAT_RGBA128;
buffer_offset = 0;
return true;
}
else {
*compression = KINC_IMAGE_COMPRESSION_NONE;
*internalFormat = 0;
stbi_io_callbacks stbi_callbacks;
stbi_callbacks.eof = stb_eof;
stbi_callbacks.read = stb_read;
stbi_callbacks.skip = stb_skip;
read_data reader;
reader.callbacks = callbacks;
reader.user_data = user_data;
int comp;
uint8_t *uncompressed = stbi_load_from_callbacks(&stbi_callbacks, &reader, width, height, &comp, 4);
if (uncompressed == NULL) {
kinc_log(KINC_LOG_LEVEL_ERROR, stbi_failure_reason());
return false;
}
for (int y = 0; y < *height; ++y) {
for (int x = 0; x < *width; ++x) {
float r = uncompressed[y * *width * 4 + x * 4 + 0] / 255.0f;
float g = uncompressed[y * *width * 4 + x * 4 + 1] / 255.0f;
float b = uncompressed[y * *width * 4 + x * 4 + 2] / 255.0f;
float a = uncompressed[y * *width * 4 + x * 4 + 3] / 255.0f;
r *= a;
g *= a;
b *= a;
output[y * *width * 4 + x * 4 + 0] = (uint8_t)kinc_round(r * 255.0f);
output[y * *width * 4 + x * 4 + 1] = (uint8_t)kinc_round(g * 255.0f);
output[y * *width * 4 + x * 4 + 2] = (uint8_t)kinc_round(b * 255.0f);
output[y * *width * 4 + x * 4 + 3] = (uint8_t)kinc_round(a * 255.0f);
}
}
*outputSize = *width * *height * 4;
buffer_offset = 0;
return true;
}
}
int kinc_image_format_sizeof(kinc_image_format_t format) {
switch (format) {
case KINC_IMAGE_FORMAT_RGBA128:
return 16;
case KINC_IMAGE_FORMAT_RGBA32:
case KINC_IMAGE_FORMAT_BGRA32:
return 4;
case KINC_IMAGE_FORMAT_RGBA64:
return 8;
case KINC_IMAGE_FORMAT_A32:
return 4;
case KINC_IMAGE_FORMAT_A16:
return 2;
case KINC_IMAGE_FORMAT_GREY8:
return 1;
case KINC_IMAGE_FORMAT_RGB24:
return 3;
}
return -1;
}
// static bool formatIsFloatingPoint(kinc_image_format_t format) {
// return format == KINC_IMAGE_FORMAT_RGBA128 || format == KINC_IMAGE_FORMAT_RGBA64 || format == KINC_IMAGE_FORMAT_A32 || format == KINC_IMAGE_FORMAT_A16;
//}
size_t kinc_image_init(kinc_image_t *image, void *memory, int width, int height, kinc_image_format_t format) {
return kinc_image_init3d(image, memory, width, height, 1, format);
}
size_t kinc_image_init3d(kinc_image_t *image, void *memory, int width, int height, int depth, kinc_image_format_t format) {
image->width = width;
image->height = height;
image->depth = depth;
image->format = format;
image->compression = KINC_IMAGE_COMPRESSION_NONE;
image->data = memory;
return width * height * depth * kinc_image_format_sizeof(format);
}
void kinc_image_init_from_bytes(kinc_image_t *image, void *data, int width, int height, kinc_image_format_t format) {
kinc_image_init_from_bytes3d(image, data, width, height, 1, format);
}
void kinc_image_init_from_bytes3d(kinc_image_t *image, void *data, int width, int height, int depth, kinc_image_format_t format) {
image->width = width;
image->height = height;
image->depth = depth;
image->format = format;
image->compression = KINC_IMAGE_COMPRESSION_NONE;
image->data = data;
}
static int read_callback(void *user_data, void *data, size_t size) {
return kinc_file_reader_read((kinc_file_reader_t *)user_data, data, size);
}
static size_t size_callback(void *user_data) {
return kinc_file_reader_size((kinc_file_reader_t *)user_data);
}
static int pos_callback(void *user_data) {
return kinc_file_reader_pos((kinc_file_reader_t *)user_data);
}
static void seek_callback(void *user_data, int pos) {
kinc_file_reader_seek((kinc_file_reader_t *)user_data, pos);
}
struct kinc_internal_image_memory {
uint8_t *data;
size_t size;
size_t offset;
};
static int memory_read_callback(void *user_data, void *data, size_t size) {
struct kinc_internal_image_memory *memory = (struct kinc_internal_image_memory *)user_data;
size_t read_size = memory->size - memory->offset < size ? memory->size - memory->offset : size;
memcpy(data, &memory->data[memory->offset], read_size);
memory->offset += read_size;
return (int)read_size;
}
static size_t memory_size_callback(void *user_data) {
struct kinc_internal_image_memory *memory = (struct kinc_internal_image_memory *)user_data;
return memory->size;
}
static int memory_pos_callback(void *user_data) {
struct kinc_internal_image_memory *memory = (struct kinc_internal_image_memory *)user_data;
return (int)memory->offset;
}
static void memory_seek_callback(void *user_data, int pos) {
struct kinc_internal_image_memory *memory = (struct kinc_internal_image_memory *)user_data;
memory->offset = (size_t)pos;
}
size_t kinc_image_size_from_callbacks(kinc_image_read_callbacks_t callbacks, void *user_data, const char *filename) {
return loadImageSize(callbacks, user_data, filename);
}
size_t kinc_image_size_from_file(const char *filename) {
kinc_file_reader_t reader;
if (kinc_file_reader_open(&reader, filename, KINC_FILE_TYPE_ASSET)) {
kinc_image_read_callbacks_t callbacks;
callbacks.read = read_callback;
callbacks.size = size_callback;
callbacks.pos = pos_callback;
callbacks.seek = seek_callback;
size_t dataSize = loadImageSize(callbacks, &reader, filename);
kinc_file_reader_close(&reader);
return dataSize;
}
return 0;
}
size_t kinc_image_size_from_encoded_bytes(void *data, size_t data_size, const char *format) {
kinc_image_read_callbacks_t callbacks;
callbacks.read = memory_read_callback;
callbacks.size = memory_size_callback;
callbacks.pos = memory_pos_callback;
callbacks.seek = memory_seek_callback;
struct kinc_internal_image_memory image_memory;
image_memory.data = (uint8_t *)data;
image_memory.size = data_size;
image_memory.offset = 0;
return loadImageSize(callbacks, &image_memory, format);
}
size_t kinc_image_init_from_callbacks(kinc_image_t *image, void *memory, kinc_image_read_callbacks_t callbacks, void *user_data, const char *filename) {
int dataSize;
loadImage(callbacks, user_data, filename, memory, &dataSize, &image->width, &image->height, &image->compression, &image->format, &image->internal_format);
image->data = memory;
return dataSize;
}
size_t kinc_image_init_from_file(kinc_image_t *image, void *memory, const char *filename) {
kinc_file_reader_t reader;
if (kinc_file_reader_open(&reader, filename, KINC_FILE_TYPE_ASSET)) {
kinc_image_read_callbacks_t callbacks;
callbacks.read = read_callback;
callbacks.size = size_callback;
callbacks.pos = pos_callback;
callbacks.seek = seek_callback;
int dataSize;
loadImage(callbacks, &reader, filename, memory, &dataSize, &image->width, &image->height, &image->compression, &image->format, &image->internal_format);
kinc_file_reader_close(&reader);
image->data = memory;
image->data_size = dataSize;
image->depth = 1;
return dataSize;
}
return 0;
}
size_t kinc_image_init_from_encoded_bytes(kinc_image_t *image, void *memory, void *data, size_t data_size, const char *format) {
kinc_image_read_callbacks_t callbacks;
callbacks.read = memory_read_callback;
callbacks.size = memory_size_callback;
callbacks.pos = memory_pos_callback;
callbacks.seek = memory_seek_callback;
struct kinc_internal_image_memory image_memory;
image_memory.data = (uint8_t *)data;
image_memory.size = data_size;
image_memory.offset = 0;
int dataSize;
loadImage(callbacks, &image_memory, format, memory, &dataSize, &image->width, &image->height, &image->compression, &image->format, &image->internal_format);
image->data = memory;
image->data_size = dataSize;
image->depth = 1;
return dataSize;
}
void kinc_image_destroy(kinc_image_t *image) {
// user has to free the data
image->data = NULL;
}
uint32_t kinc_image_at(kinc_image_t *image, int x, int y) {
if (image->data == NULL) {
return 0;
}
else {
return *(uint32_t *)&((uint8_t *)image->data)[image->width * kinc_image_format_sizeof(image->format) * y + x * kinc_image_format_sizeof(image->format)];
}
}
void *kinc_image_at_raw(kinc_image_t *image, int x, int y) {
if (image->data == NULL) {
return NULL;
}
else {
return &((uint8_t *)image->data)[image->width * kinc_image_format_sizeof(image->format) * y + x * kinc_image_format_sizeof(image->format)];
}
}
uint8_t *kinc_image_get_pixels(kinc_image_t *image) {
return (uint8_t *)image->data;
}
#endif
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,45 @@
#pragma once
#include <kinc/global.h>
/*! \file acceleration.h
\brief Provides data provided by acceleration-sensors.
*/
#ifdef __cplusplus
extern "C" {
#endif
/// <summary>
/// Sets the acceleration-callback which is called with measured acceleration-data in three dimensions.
/// </summary>
/// <param name="value">The callback</param>
KINC_FUNC void kinc_acceleration_set_callback(void (*value)(float /*x*/, float /*y*/, float /*z*/));
void kinc_internal_on_acceleration(float x, float y, float z);
#ifdef KINC_IMPLEMENTATION_INPUT
#define KINC_IMPLEMENTATION
#endif
#ifdef KINC_IMPLEMENTATION
#include <memory.h>
void (*acceleration_callback)(float /*x*/, float /*y*/, float /*z*/) = NULL;
void kinc_acceleration_set_callback(void (*value)(float /*x*/, float /*y*/, float /*z*/)) {
acceleration_callback = value;
}
void kinc_internal_on_acceleration(float x, float y, float z) {
if (acceleration_callback != NULL) {
acceleration_callback(x, y, z);
}
}
#endif
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,92 @@
#pragma once
#include <kinc/global.h>
/*! \file gamepad.h
\brief Provides gamepad-support.
*/
#ifdef __cplusplus
extern "C" {
#endif
/// <summary>
/// Sets the gamepad-axis-callback which is called with data about changing gamepad-sticks.
/// </summary>
/// <param name="value">The callback</param>
KINC_FUNC void kinc_gamepad_set_axis_callback(void (*value)(int /*gamepad*/, int /*axis*/, float /*value*/));
/// <summary>
/// Sets the gamepad-button-callback which is called with data about changing gamepad-buttons.
/// </summary>
/// <param name="value">The callback</param>
KINC_FUNC void kinc_gamepad_set_button_callback(void (*value)(int /*gamepad*/, int /*button*/, float /*value*/));
/// <summary>
/// Returns a vendor-name for a gamepad.
/// </summary>
/// <param name="gamepad">The index of the gamepad for which to receive the vendor-name</param>
/// <returns>The vendor-name</returns>
KINC_FUNC const char *kinc_gamepad_vendor(int gamepad);
/// <summary>
/// Returns a name for a gamepad.
/// </summary>
/// <param name="gamepad">The index of the gamepad for which to receive the name</param>
/// <returns>The gamepad's name</returns>
KINC_FUNC const char *kinc_gamepad_product_name(int gamepad);
/// <summary>
/// Checks whether a gamepad is connected.
/// </summary>
/// <param name="gamepad">The index of the gamepad which's connection will be checked</param>
/// <returns>Whether a gamepad is connected for the gamepad-index</returns>
KINC_FUNC bool kinc_gamepad_connected(int gamepad);
/// <summary>
/// Rumbles a gamepad. Careful here because it might just fall off your table.
/// </summary>
/// <param name="gamepad">The index of the gamepad to rumble</param>
/// <param name="left">Rumble-strength for the left motor between 0 and 1</param>
/// <param name="right">Rumble-strength for the right motor between 0 and 1</param>
KINC_FUNC void kinc_gamepad_rumble(int gamepad, float left, float right);
void kinc_internal_gamepad_trigger_axis(int gamepad, int axis, float value);
void kinc_internal_gamepad_trigger_button(int gamepad, int button, float value);
#ifdef KINC_IMPLEMENTATION_INPUT
#define KINC_IMPLEMENTATION
#endif
#ifdef KINC_IMPLEMENTATION
#include <memory.h>
static void (*gamepad_axis_callback)(int /*gamepad*/, int /*axis*/, float /*value*/) = NULL;
static void (*gamepad_button_callback)(int /*gamepad*/, int /*button*/, float /*value*/) = NULL;
void kinc_gamepad_set_axis_callback(void (*value)(int /*gamepad*/, int /*axis*/, float /*value*/)) {
gamepad_axis_callback = value;
}
void kinc_gamepad_set_button_callback(void (*value)(int /*gamepad*/, int /*button*/, float /*value*/)) {
gamepad_button_callback = value;
}
void kinc_internal_gamepad_trigger_axis(int gamepad, int axis, float value) {
if (gamepad_axis_callback != NULL) {
gamepad_axis_callback(gamepad, axis, value);
}
}
void kinc_internal_gamepad_trigger_button(int gamepad, int button, float value) {
if (gamepad_button_callback != NULL) {
gamepad_button_callback(gamepad, button, value);
}
}
#endif
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,9 @@
#define KINC_IMPLEMENTATION_INPUT
#include "acceleration.h"
#include "gamepad.h"
#include "keyboard.h"
#include "mouse.h"
#include "pen.h"
#include "rotation.h"
#include "surface.h"

View File

@ -0,0 +1,293 @@
#pragma once
#include <kinc/global.h>
/*! \file keyboard.h
\brief Provides keyboard-support.
*/
#ifdef __cplusplus
extern "C" {
#endif
#define KINC_KEY_UNKNOWN 0
#define KINC_KEY_BACK 1 // Android
#define KINC_KEY_CANCEL 3
#define KINC_KEY_HELP 6
#define KINC_KEY_BACKSPACE 8
#define KINC_KEY_TAB 9
#define KINC_KEY_CLEAR 12
#define KINC_KEY_RETURN 13
#define KINC_KEY_SHIFT 16
#define KINC_KEY_CONTROL 17
#define KINC_KEY_ALT 18
#define KINC_KEY_PAUSE 19
#define KINC_KEY_CAPS_LOCK 20
#define KINC_KEY_KANA 21
#define KINC_KEY_HANGUL 21
#define KINC_KEY_EISU 22
#define KINC_KEY_JUNJA 23
#define KINC_KEY_FINAL 24
#define KINC_KEY_HANJA 25
#define KINC_KEY_KANJI 25
#define KINC_KEY_ESCAPE 27
#define KINC_KEY_CONVERT 28
#define KINC_KEY_NON_CONVERT 29
#define KINC_KEY_ACCEPT 30
#define KINC_KEY_MODE_CHANGE 31
#define KINC_KEY_SPACE 32
#define KINC_KEY_PAGE_UP 33
#define KINC_KEY_PAGE_DOWN 34
#define KINC_KEY_END 35
#define KINC_KEY_HOME 36
#define KINC_KEY_LEFT 37
#define KINC_KEY_UP 38
#define KINC_KEY_RIGHT 39
#define KINC_KEY_DOWN 40
#define KINC_KEY_SELECT 41
#define KINC_KEY_PRINT 42
#define KINC_KEY_EXECUTE 43
#define KINC_KEY_PRINT_SCREEN 44
#define KINC_KEY_INSERT 45
#define KINC_KEY_DELETE 46
#define KINC_KEY_0 48
#define KINC_KEY_1 49
#define KINC_KEY_2 50
#define KINC_KEY_3 51
#define KINC_KEY_4 52
#define KINC_KEY_5 53
#define KINC_KEY_6 54
#define KINC_KEY_7 55
#define KINC_KEY_8 56
#define KINC_KEY_9 57
#define KINC_KEY_COLON 58
#define KINC_KEY_SEMICOLON 59
#define KINC_KEY_LESS_THAN 60
#define KINC_KEY_EQUALS 61
#define KINC_KEY_GREATER_THAN 62
#define KINC_KEY_QUESTIONMARK 63
#define KINC_KEY_AT 64
#define KINC_KEY_A 65
#define KINC_KEY_B 66
#define KINC_KEY_C 67
#define KINC_KEY_D 68
#define KINC_KEY_E 69
#define KINC_KEY_F 70
#define KINC_KEY_G 71
#define KINC_KEY_H 72
#define KINC_KEY_I 73
#define KINC_KEY_J 74
#define KINC_KEY_K 75
#define KINC_KEY_L 76
#define KINC_KEY_M 77
#define KINC_KEY_N 78
#define KINC_KEY_O 79
#define KINC_KEY_P 80
#define KINC_KEY_Q 81
#define KINC_KEY_R 82
#define KINC_KEY_S 83
#define KINC_KEY_T 84
#define KINC_KEY_U 85
#define KINC_KEY_V 86
#define KINC_KEY_W 87
#define KINC_KEY_X 88
#define KINC_KEY_Y 89
#define KINC_KEY_Z 90
#define KINC_KEY_WIN 91
#define KINC_KEY_CONTEXT_MENU 93
#define KINC_KEY_SLEEP 95
#define KINC_KEY_NUMPAD_0 96
#define KINC_KEY_NUMPAD_1 97
#define KINC_KEY_NUMPAD_2 98
#define KINC_KEY_NUMPAD_3 99
#define KINC_KEY_NUMPAD_4 100
#define KINC_KEY_NUMPAD_5 101
#define KINC_KEY_NUMPAD_6 102
#define KINC_KEY_NUMPAD_7 103
#define KINC_KEY_NUMPAD_8 104
#define KINC_KEY_NUMPAD_9 105
#define KINC_KEY_MULTIPLY 106
#define KINC_KEY_ADD 107
#define KINC_KEY_SEPARATOR 108
#define KINC_KEY_SUBTRACT 109
#define KINC_KEY_DECIMAL 110
#define KINC_KEY_DIVIDE 111
#define KINC_KEY_F1 112
#define KINC_KEY_F2 113
#define KINC_KEY_F3 114
#define KINC_KEY_F4 115
#define KINC_KEY_F5 116
#define KINC_KEY_F6 117
#define KINC_KEY_F7 118
#define KINC_KEY_F8 119
#define KINC_KEY_F9 120
#define KINC_KEY_F10 121
#define KINC_KEY_F11 122
#define KINC_KEY_F12 123
#define KINC_KEY_F13 124
#define KINC_KEY_F14 125
#define KINC_KEY_F15 126
#define KINC_KEY_F16 127
#define KINC_KEY_F17 128
#define KINC_KEY_F18 129
#define KINC_KEY_F19 130
#define KINC_KEY_F20 131
#define KINC_KEY_F21 132
#define KINC_KEY_F22 133
#define KINC_KEY_F23 134
#define KINC_KEY_F24 135
#define KINC_KEY_NUM_LOCK 144
#define KINC_KEY_SCROLL_LOCK 145
#define KINC_KEY_WIN_OEM_FJ_JISHO 146
#define KINC_KEY_WIN_OEM_FJ_MASSHOU 147
#define KINC_KEY_WIN_OEM_FJ_TOUROKU 148
#define KINC_KEY_WIN_OEM_FJ_LOYA 149
#define KINC_KEY_WIN_OEM_FJ_ROYA 150
#define KINC_KEY_CIRCUMFLEX 160
#define KINC_KEY_EXCLAMATION 161
#define KINC_KEY_DOUBLE_QUOTE 162
#define KINC_KEY_HASH 163
#define KINC_KEY_DOLLAR 164
#define KINC_KEY_PERCENT 165
#define KINC_KEY_AMPERSAND 166
#define KINC_KEY_UNDERSCORE 167
#define KINC_KEY_OPEN_PAREN 168
#define KINC_KEY_CLOSE_PAREN 169
#define KINC_KEY_ASTERISK 170
#define KINC_KEY_PLUS 171
#define KINC_KEY_PIPE 172
#define KINC_KEY_HYPHEN_MINUS 173
#define KINC_KEY_OPEN_CURLY_BRACKET 174
#define KINC_KEY_CLOSE_CURLY_BRACKET 175
#define KINC_KEY_TILDE 176
#define KINC_KEY_VOLUME_MUTE 181
#define KINC_KEY_VOLUME_DOWN 182
#define KINC_KEY_VOLUME_UP 183
#define KINC_KEY_COMMA 188
#define KINC_KEY_PERIOD 190
#define KINC_KEY_SLASH 191
#define KINC_KEY_BACK_QUOTE 192
#define KINC_KEY_OPEN_BRACKET 219
#define KINC_KEY_BACK_SLASH 220
#define KINC_KEY_CLOSE_BRACKET 221
#define KINC_KEY_QUOTE 222
#define KINC_KEY_META 224
#define KINC_KEY_ALT_GR 225
#define KINC_KEY_WIN_ICO_HELP 227
#define KINC_KEY_WIN_ICO_00 228
#define KINC_KEY_WIN_ICO_CLEAR 230
#define KINC_KEY_WIN_OEM_RESET 233
#define KINC_KEY_WIN_OEM_JUMP 234
#define KINC_KEY_WIN_OEM_PA1 235
#define KINC_KEY_WIN_OEM_PA2 236
#define KINC_KEY_WIN_OEM_PA3 237
#define KINC_KEY_WIN_OEM_WSCTRL 238
#define KINC_KEY_WIN_OEM_CUSEL 239
#define KINC_KEY_WIN_OEM_ATTN 240
#define KINC_KEY_WIN_OEM_FINISH 241
#define KINC_KEY_WIN_OEM_COPY 242
#define KINC_KEY_WIN_OEM_AUTO 243
#define KINC_KEY_WIN_OEM_ENLW 244
#define KINC_KEY_WIN_OEM_BACK_TAB 245
#define KINC_KEY_ATTN 246
#define KINC_KEY_CRSEL 247
#define KINC_KEY_EXSEL 248
#define KINC_KEY_EREOF 249
#define KINC_KEY_PLAY 250
#define KINC_KEY_ZOOM 251
#define KINC_KEY_PA1 253
#define KINC_KEY_WIN_OEM_CLEAR 254
/// <summary>
/// Show the keyboard if the system is using a software-keyboard.
/// </summary>
KINC_FUNC void kinc_keyboard_show(void);
/// <summary>
/// Hide the keyboard if the system is using a software-keyboard.
/// </summary>
KINC_FUNC void kinc_keyboard_hide(void);
/// <summary>
/// Figure out whether the keyboard is currently shown if the system is using a software-keyboard.
/// </summary>
/// <returns>Whether the keyboard is currently shown</returns>
KINC_FUNC bool kinc_keyboard_active(void);
/// <summary>
/// Sets the keyboard-key-down-callback which is called with a key-code when a key goes down. Do not use this for text-input, that's what the key-press-callback
/// is here for.
/// </summary>
/// <param name="value">The callback</param>
KINC_FUNC void kinc_keyboard_set_key_down_callback(void (*value)(int /*key_code*/, void * /*data*/), void *data);
/// <summary>
/// Sets the keyboard-key-up-callback which is called with a key-code when a key goes up. Do not use this for text-input, that's what the key-press-callback is
/// here for.
/// </summary>
/// <param name="value">The callback</param>
KINC_FUNC void kinc_keyboard_set_key_up_callback(void (*value)(int /*key_code*/, void * /*data*/), void *data);
/// <summary>
/// Sets the keyboard-key-press-callback which is called when the system decides that a character came in via the keyboard. Use this for text-input.
/// </summary>
/// <param name="value">The callback</param>
KINC_FUNC void kinc_keyboard_set_key_press_callback(void (*value)(unsigned /*character*/, void * /*data*/), void *data);
void kinc_internal_keyboard_trigger_key_down(int key_code);
void kinc_internal_keyboard_trigger_key_up(int key_code);
void kinc_internal_keyboard_trigger_key_press(unsigned character);
#ifdef KINC_IMPLEMENTATION_INPUT
#define KINC_IMPLEMENTATION
#endif
#ifdef KINC_IMPLEMENTATION
#include <memory.h>
static void (*keyboard_key_down_callback)(int /*key_code*/, void * /*data*/) = NULL;
static void *keyboard_key_down_callback_data = NULL;
static void (*keyboard_key_up_callback)(int /*key_code*/, void * /*data*/) = NULL;
static void *keyboard_key_up_callback_data = NULL;
static void (*keyboard_key_press_callback)(unsigned /*character*/, void * /*data*/) = NULL;
static void *keyboard_key_press_callback_data = NULL;
void kinc_keyboard_set_key_down_callback(void (*value)(int /*key_code*/, void * /*data*/), void *data) {
keyboard_key_down_callback = value;
keyboard_key_down_callback_data = data;
}
void kinc_keyboard_set_key_up_callback(void (*value)(int /*key_code*/, void * /*data*/), void *data) {
keyboard_key_up_callback = value;
keyboard_key_up_callback_data = data;
}
void kinc_keyboard_set_key_press_callback(void (*value)(unsigned /*character*/, void * /*data*/), void *data) {
keyboard_key_press_callback = value;
keyboard_key_press_callback_data = data;
}
void kinc_internal_keyboard_trigger_key_down(int key_code) {
if (keyboard_key_down_callback != NULL) {
keyboard_key_down_callback(key_code, keyboard_key_down_callback_data);
}
}
void kinc_internal_keyboard_trigger_key_up(int key_code) {
if (keyboard_key_up_callback != NULL) {
keyboard_key_up_callback(key_code, keyboard_key_up_callback_data);
}
}
void kinc_internal_keyboard_trigger_key_press(unsigned character) {
if (keyboard_key_press_callback != NULL) {
keyboard_key_press_callback(character, keyboard_key_press_callback_data);
}
}
#endif
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,296 @@
#pragma once
#include <kinc/global.h>
#include <stdbool.h>
/*! \file mouse.h
\brief Provides mouse-support.
*/
#ifdef __cplusplus
extern "C" {
#endif
#define KINC_MOUSE_LEFT 0
#define KINC_MOUSE_RIGHT 1
#define KINC_MOUSE_MIDDLE 2
// eg backward sidebutton
#define KINC_MOUSE_EXTRA1 3
// eg forward sidebutton
#define KINC_MOUSE_EXTRA2 4
/// <summary>
/// Sets the mouse-press-callback which is called when a mouse-button is pressed.
/// The button callback argument will usually contain one of the KINC_MOUSE_* values,
/// but can contain higher values on certain platforms when mice with a lot of buttons are used.
/// </summary>
/// <param name="value">The callback</param>
KINC_FUNC void kinc_mouse_set_press_callback(void (*value)(int /*window*/, int /*button*/, int /*x*/, int /*y*/, void * /*data*/), void *data);
/// <summary>
/// Sets the mouse-release-callback which is called when a mouse-button is released.
/// The button callback argument will usually contain one of the KINC_MOUSE_* values,
/// but can contain higher values on certain platforms when mice with a lot of buttons are used.
/// </summary>
/// <param name="value">The callback</param>
KINC_FUNC void kinc_mouse_set_release_callback(void (*value)(int /*window*/, int /*button*/, int /*x*/, int /*y*/, void * /*data*/), void *data);
/// <summary>
/// Sets the mouse-move-callback which is called when the mouse is moved.
/// </summary>
/// <param name="value">The callback</param>
KINC_FUNC void kinc_mouse_set_move_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, int /*movement_x*/, int /*movement_y*/, void * /*data*/),
void *data);
/// <summary>
/// Sets the mouse-scroll-callback which is called when the mouse wheel is spinning.
/// </summary>
/// <param name="value">The callback</param>
KINC_FUNC void kinc_mouse_set_scroll_callback(void (*value)(int /*window*/, int /*delta*/, void * /*data*/), void *data);
/// <summary>
/// Sets the mouse-enter-window-callback which is called when the mouse-cursor enters the application-window.
/// </summary>
/// <param name="value">The callback</param>
KINC_FUNC void kinc_mouse_set_enter_window_callback(void (*value)(int /*window*/, void * /*data*/), void *data);
/// <summary>
/// Sets the mouse-leave-window-callback which is called when the mouse-cursor leaves the application-window.
/// </summary>
/// <param name="value">The callback</param>
KINC_FUNC void kinc_mouse_set_leave_window_callback(void (*value)(int /*window*/, void * /*data*/), void *data);
/// <summary>
/// Figures out whether mouse-locking is supported.
/// </summary>
/// <returns>Whether mouse-locking is supported</returns>
KINC_FUNC bool kinc_mouse_can_lock(void);
/// <summary>
/// Figures out whether the mouse is currently locked.
/// </summary>
/// <returns>Whether the mouse is currently locked</returns>
KINC_FUNC bool kinc_mouse_is_locked(void);
/// <summary>
/// Locks the mouse to a window.
/// </summary>
/// <param name="window">The window to lock the mouse to</param>
KINC_FUNC void kinc_mouse_lock(int window);
/// <summary>
/// Unlocks the mouse.
/// </summary>
/// <param name=""></param>
/// <returns></returns>
KINC_FUNC void kinc_mouse_unlock(void);
/// <summary>
/// Change the cursor-image to something that's semi-randomly based on the provided int.
/// </summary>
/// <param name="cursor">Defines what the cursor is changed to - somehow</param>
KINC_FUNC void kinc_mouse_set_cursor(int cursor);
/// <summary>
/// Shows the mouse-cursor.
/// </summary>
KINC_FUNC void kinc_mouse_show(void);
/// <summary>
/// Hides the mouse-cursor.
/// </summary>
KINC_FUNC void kinc_mouse_hide(void);
/// <summary>
/// Manually sets the mouse-cursor-position.
/// </summary>
/// <param name="window">The window to place the cursor in</param>
/// <param name="x">The x-position inside the window to place the cursor at</param>
/// <param name="y">The y-position inside the window to place the cursor at</param>
KINC_FUNC void kinc_mouse_set_position(int window, int x, int y);
/// <summary>
/// Gets the current mouse-position relative to a window.
/// </summary>
/// <param name="window">The window to base the returned position on</param>
/// <param name="x">A pointer where the cursor's x-position is put into</param>
/// <param name="y">A pointer where the cursor's y-position is put into</param>
KINC_FUNC void kinc_mouse_get_position(int window, int *x, int *y);
void kinc_internal_mouse_trigger_press(int window, int button, int x, int y);
void kinc_internal_mouse_trigger_release(int window, int button, int x, int y);
void kinc_internal_mouse_trigger_move(int window, int x, int y);
void kinc_internal_mouse_trigger_scroll(int window, int delta);
void kinc_internal_mouse_trigger_enter_window(int window);
void kinc_internal_mouse_trigger_leave_window(int window);
void kinc_internal_mouse_lock(int window);
void kinc_internal_mouse_unlock(void);
void kinc_internal_mouse_window_activated(int window);
void kinc_internal_mouse_window_deactivated(int window);
#ifdef KINC_IMPLEMENTATION_INPUT
#define KINC_IMPLEMENTATION
#endif
#ifdef KINC_IMPLEMENTATION
#undef KINC_IMPLEMENTATION
#include <kinc/window.h>
#define KINC_IMPLEMENTATION
#include <memory.h>
static void (*mouse_press_callback)(int /*window*/, int /*button*/, int /*x*/, int /*y*/, void * /*data*/) = NULL;
static void *mouse_press_callback_data = NULL;
static void (*mouse_release_callback)(int /*window*/, int /*button*/, int /*x*/, int /*y*/, void * /*data*/) = NULL;
static void *mouse_release_callback_data = NULL;
static void (*mouse_move_callback)(int /*window*/, int /*x*/, int /*y*/, int /*movementX*/, int /*movementY*/, void * /*data*/) = NULL;
static void *mouse_move_callback_data = NULL;
static void (*mouse_scroll_callback)(int /*window*/, int /*delta*/, void * /*data*/) = NULL;
static void *mouse_scroll_callback_data = NULL;
static void (*mouse_enter_window_callback)(int /*window*/, void * /*data*/) = NULL;
static void *mouse_enter_window_callback_data = NULL;
static void (*mouse_leave_window_callback)(int /*window*/, void * /*data*/) = NULL;
static void *mouse_leave_window_callback_data = NULL;
void kinc_mouse_set_press_callback(void (*value)(int /*window*/, int /*button*/, int /*x*/, int /*y*/, void * /*data*/), void *data) {
mouse_press_callback = value;
mouse_press_callback_data = data;
}
void kinc_mouse_set_release_callback(void (*value)(int /*window*/, int /*button*/, int /*x*/, int /*y*/, void * /*data*/), void *data) {
mouse_release_callback = value;
mouse_release_callback_data = data;
}
void kinc_mouse_set_move_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, int /*movement_x*/, int /*movement_y*/, void * /*data*/), void *data) {
mouse_move_callback = value;
mouse_move_callback_data = data;
}
void kinc_mouse_set_scroll_callback(void (*value)(int /*window*/, int /*delta*/, void * /*data*/), void *data) {
mouse_scroll_callback = value;
mouse_scroll_callback_data = data;
}
void kinc_mouse_set_enter_window_callback(void (*value)(int /*window*/, void * /*data*/), void *data) {
mouse_enter_window_callback = value;
mouse_enter_window_callback_data = data;
}
void kinc_mouse_set_leave_window_callback(void (*value)(int /*window*/, void * /*data*/), void *data) {
mouse_leave_window_callback = value;
mouse_leave_window_callback_data = data;
}
void kinc_internal_mouse_trigger_release(int window, int button, int x, int y) {
if (mouse_release_callback != NULL) {
mouse_release_callback(window, button, x, y, mouse_release_callback_data);
}
}
void kinc_internal_mouse_trigger_scroll(int window, int delta) {
if (mouse_scroll_callback != NULL) {
mouse_scroll_callback(window, delta, mouse_scroll_callback_data);
}
}
void kinc_internal_mouse_trigger_enter_window(int window) {
if (mouse_enter_window_callback != NULL) {
mouse_enter_window_callback(window, mouse_enter_window_callback_data);
}
}
void kinc_internal_mouse_trigger_leave_window(int window) {
if (mouse_leave_window_callback != NULL) {
mouse_leave_window_callback(window, mouse_leave_window_callback_data);
}
}
void kinc_internal_mouse_window_activated(int window) {
if (kinc_mouse_is_locked()) {
kinc_internal_mouse_lock(window);
}
}
void kinc_internal_mouse_window_deactivated(int window) {
if (kinc_mouse_is_locked()) {
kinc_internal_mouse_unlock();
}
}
// TODO: handle state per window
static bool moved = false;
static bool locked = false;
static int preLockWindow = 0;
static int preLockX = 0;
static int preLockY = 0;
static int centerX = 0;
static int centerY = 0;
static int lastX = 0;
static int lastY = 0;
void kinc_internal_mouse_trigger_press(int window, int button, int x, int y) {
lastX = x;
lastY = y;
if (mouse_press_callback != NULL) {
mouse_press_callback(window, button, x, y, mouse_press_callback_data);
}
}
void kinc_internal_mouse_trigger_move(int window, int x, int y) {
int movementX = 0;
int movementY = 0;
if (kinc_mouse_is_locked()) {
movementX = x - centerX;
movementY = y - centerY;
if (movementX != 0 || movementY != 0) {
kinc_mouse_set_position(window, centerX, centerY);
x = centerX;
y = centerY;
}
}
else if (moved) {
movementX = x - lastX;
movementY = y - lastY;
}
moved = true;
lastX = x;
lastY = y;
if (mouse_move_callback != NULL && (movementX != 0 || movementY != 0)) {
mouse_move_callback(window, x, y, movementX, movementY, mouse_move_callback_data);
}
}
bool kinc_mouse_is_locked(void) {
return locked;
}
void kinc_mouse_lock(int window) {
if (!kinc_mouse_can_lock()) {
return;
}
locked = true;
kinc_internal_mouse_lock(window);
kinc_mouse_get_position(window, &preLockX, &preLockY);
centerX = kinc_window_width(window) / 2;
centerY = kinc_window_height(window) / 2;
kinc_mouse_set_position(window, centerX, centerY);
}
void kinc_mouse_unlock(void) {
if (!kinc_mouse_can_lock()) {
return;
}
moved = false;
locked = false;
kinc_internal_mouse_unlock();
kinc_mouse_set_position(preLockWindow, preLockX, preLockY);
}
#endif
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,137 @@
#pragma once
#include <kinc/global.h>
/*! \file pen.h
\brief Provides pen-support.
*/
#ifdef __cplusplus
extern "C" {
#endif
/// <summary>
/// Sets the pen-press-callback which is called when the pen is touching the drawing-surface.
/// </summary>
/// <param name="value">The callback</param>
KINC_FUNC void kinc_pen_set_press_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/));
/// <summary>
/// Sets the pen-move-callback which is called when the pen is moved.
/// </summary>
/// <param name="value">The callback</param>
KINC_FUNC void kinc_pen_set_move_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/));
/// <summary>
/// Sets the pen-release-callback which is called when the pen is moved away from the drawing-surface.
/// </summary>
/// <param name="value">The callback</param>
KINC_FUNC void kinc_pen_set_release_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/));
/// <summary>
/// Sets the eraser-press-callback which is called when the pen is touching the drawing-surface in eraser-mode.
/// </summary>
/// <param name="value">The callback</param>
KINC_FUNC void kinc_eraser_set_press_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/));
/// <summary>
/// Sets the eraser-move-callback which is called when the pen is moved while in eraser-mode.
/// </summary>
/// <param name="value">The callback</param>
KINC_FUNC void kinc_eraser_set_move_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/));
/// <summary>
/// Sets the eraser-release-callback which is called when the pen is moved away from the drawing-surface when in eraser-mode.
/// </summary>
/// <param name="value">The callback</param>
KINC_FUNC void kinc_eraser_set_release_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/));
void kinc_internal_pen_trigger_move(int window, int x, int y, float pressure);
void kinc_internal_pen_trigger_press(int window, int x, int y, float pressure);
void kinc_internal_pen_trigger_release(int window, int x, int y, float pressure);
void kinc_internal_eraser_trigger_move(int window, int x, int y, float pressure);
void kinc_internal_eraser_trigger_press(int window, int x, int y, float pressure);
void kinc_internal_eraser_trigger_release(int window, int x, int y, float pressure);
#ifdef KINC_IMPLEMENTATION_INPUT
#define KINC_IMPLEMENTATION
#endif
#ifdef KINC_IMPLEMENTATION
#include <memory.h>
static void (*pen_press_callback)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/) = NULL;
static void (*pen_move_callback)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/) = NULL;
static void (*pen_release_callback)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/) = NULL;
static void (*eraser_press_callback)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/) = NULL;
static void (*eraser_move_callback)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/) = NULL;
static void (*eraser_release_callback)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/) = NULL;
void kinc_pen_set_press_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/)) {
pen_press_callback = value;
}
void kinc_pen_set_move_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/)) {
pen_move_callback = value;
}
void kinc_pen_set_release_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/)) {
pen_release_callback = value;
}
void kinc_eraser_set_press_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/)) {
eraser_press_callback = value;
}
void kinc_eraser_set_move_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/)) {
eraser_move_callback = value;
}
void kinc_eraser_set_release_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/)) {
eraser_release_callback = value;
}
void kinc_internal_pen_trigger_press(int window, int x, int y, float pressure) {
if (pen_press_callback != NULL) {
pen_press_callback(window, x, y, pressure);
}
}
void kinc_internal_pen_trigger_move(int window, int x, int y, float pressure) {
if (pen_move_callback != NULL) {
pen_move_callback(window, x, y, pressure);
}
}
void kinc_internal_pen_trigger_release(int window, int x, int y, float pressure) {
if (pen_release_callback != NULL) {
pen_release_callback(window, x, y, pressure);
}
}
void kinc_internal_eraser_trigger_press(int window, int x, int y, float pressure) {
if (eraser_press_callback != NULL) {
eraser_press_callback(window, x, y, pressure);
}
}
void kinc_internal_eraser_trigger_move(int window, int x, int y, float pressure) {
if (eraser_move_callback != NULL) {
eraser_move_callback(window, x, y, pressure);
}
}
void kinc_internal_eraser_trigger_release(int window, int x, int y, float pressure) {
if (eraser_release_callback != NULL) {
eraser_release_callback(window, x, y, pressure);
}
}
#endif
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,45 @@
#pragma once
#include <kinc/global.h>
/*! \file rotation.h
\brief Provides support for rotation-sensors.
*/
#ifdef __cplusplus
extern "C" {
#endif
/// <summary>
/// Sets the rotation-callback which is called when the device is rotated. Act quickly when this is called for a desktop-system.
/// </summary>
/// <param name="value">The callback</param>
KINC_FUNC void kinc_rotation_set_callback(void (*value)(float /*x*/, float /*y*/, float /*z*/));
void kinc_internal_on_rotation(float x, float y, float z);
#ifdef KINC_IMPLEMENTATION_INPUT
#define KINC_IMPLEMENTATION
#endif
#ifdef KINC_IMPLEMENTATION
#include <memory.h>
static void (*rotation_callback)(float /*x*/, float /*y*/, float /*z*/) = NULL;
void kinc_rotation_set_callback(void (*value)(float /*x*/, float /*y*/, float /*z*/)) {
rotation_callback = value;
}
void kinc_internal_on_rotation(float x, float y, float z) {
if (rotation_callback != NULL) {
rotation_callback(x, y, z);
}
}
#endif
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,81 @@
#pragma once
#include <kinc/global.h>
/*! \file surface.h
\brief Provides touch-support.
*/
#ifdef __cplusplus
extern "C" {
#endif
/// <summary>
/// Sets the surface-touch-start-callback which is called when a finger-touch is detected.
/// </summary>
/// <param name="value">The callback</param>
KINC_FUNC void kinc_surface_set_touch_start_callback(void (*value)(int /*index*/, int /*x*/, int /*y*/));
/// <summary>
/// Sets the surface-move-callback which is called when a finger is moving around.
/// </summary>
/// <param name="value">The callback</param>
KINC_FUNC void kinc_surface_set_move_callback(void (*value)(int /*index*/, int /*x*/, int /*y*/));
/// <summary>
/// Sets the surface-touch-end-callback which is called when a finger disappears. This is usually not a medical emergency.
/// </summary>
/// <param name="value">The callback</param>
KINC_FUNC void kinc_surface_set_touch_end_callback(void (*value)(int /*index*/, int /*x*/, int /*y*/));
void kinc_internal_surface_trigger_touch_start(int index, int x, int y);
void kinc_internal_surface_trigger_move(int index, int x, int y);
void kinc_internal_surface_trigger_touch_end(int index, int x, int y);
#ifdef KINC_IMPLEMENTATION_INPUT
#define KINC_IMPLEMENTATION
#endif
#ifdef KINC_IMPLEMENTATION
#include <memory.h>
static void (*surface_touch_start_callback)(int /*index*/, int /*x*/, int /*y*/) = NULL;
static void (*surface_move_callback)(int /*index*/, int /*x*/, int /*y*/) = NULL;
static void (*surface_touch_end_callback)(int /*index*/, int /*x*/, int /*y*/) = NULL;
void kinc_surface_set_touch_start_callback(void (*value)(int /*index*/, int /*x*/, int /*y*/)) {
surface_touch_start_callback = value;
}
void kinc_surface_set_move_callback(void (*value)(int /*index*/, int /*x*/, int /*y*/)) {
surface_move_callback = value;
}
void kinc_surface_set_touch_end_callback(void (*value)(int /*index*/, int /*x*/, int /*y*/)) {
surface_touch_end_callback = value;
}
void kinc_internal_surface_trigger_touch_start(int index, int x, int y) {
if (surface_touch_start_callback != NULL) {
surface_touch_start_callback(index, x, y);
}
}
void kinc_internal_surface_trigger_move(int index, int x, int y) {
if (surface_move_callback != NULL) {
surface_move_callback(index, x, y);
}
}
void kinc_internal_surface_trigger_touch_end(int index, int x, int y) {
if (surface_touch_end_callback != NULL) {
surface_touch_end_callback(index, x, y);
}
}
#endif
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,563 @@
#pragma once
#include <kinc/global.h>
#if defined(KORE_SONY) || defined(KORE_SWITCH)
#include <kinc/backend/FileReaderImpl.h>
#endif
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
/*! \file filereader.h
\brief Provides an API very similar to fread and friends but handles the intricacies of where files are actually hidden on each supported system.
*/
#ifdef __cplusplus
extern "C" {
#endif
#ifndef KORE_DEBUGDIR
#define KORE_DEBUGDIR "Deployment"
#endif
#ifdef KORE_ANDROID
struct AAsset;
struct __sFILE;
typedef struct __sFILE FILE;
#endif
#define KINC_FILE_TYPE_ASSET 0
#define KINC_FILE_TYPE_SAVE 1
#ifdef KORE_ANDROID
typedef struct kinc_file_reader {
int pos;
int size;
FILE *file;
struct AAsset *asset;
int type;
} kinc_file_reader_t;
#else
typedef struct kinc_file_reader {
void *file;
int size;
int type;
int mode;
bool mounted;
#if defined(KORE_SONY) || defined(KORE_SWITCH)
kinc_file_reader_impl_t impl;
#endif
} kinc_file_reader_t;
#endif
/// <summary>
/// Opens a file for reading.
/// </summary>
/// <param name="reader">The reader to initialize for reading</param>
/// <param name="filepath">A filepath to identify a file</param>
/// <param name="type">Looks for a regular file (KINC_FILE_TYPE_ASSET) or a save-file (KINC_FILE_TYPE_SAVE)</param>
/// <returns>Whether the file could be opened</returns>
KINC_FUNC bool kinc_file_reader_open(kinc_file_reader_t *reader, const char *filepath, int type);
/// <summary>
/// Closes a file.
/// </summary>
/// <param name="reader">The file to close</param>
KINC_FUNC void kinc_file_reader_close(kinc_file_reader_t *reader);
/// <summary>
/// Reads data from a file starting from the current reading-position and increases the reading-position accordingly.
/// </summary>
/// <param name="reader">The reader to read from</param>
/// <param name="data">A pointer to write the data to</param>
/// <param name="size">The amount of data to read in bytes</param>
/// <returns>The number of bytes that were read - can be less than size if there is not enough data in the file</returns>
KINC_FUNC int kinc_file_reader_read(kinc_file_reader_t *reader, void *data, size_t size);
/// <summary>
/// Figures out the size of a file.
/// </summary>
/// <param name="reader">The reader which's file-size to figure out</param>
/// <returns>The size in bytes</returns>
KINC_FUNC size_t kinc_file_reader_size(kinc_file_reader_t *reader);
/// <summary>
/// Figures out the current reading-position in the file.
/// </summary>
/// <param name="reader">The reader which's reading-position to figure out</param>
/// <returns>The current reading-position</returns>
KINC_FUNC int kinc_file_reader_pos(kinc_file_reader_t *reader);
/// <summary>
/// Sets the reading-position manually.
/// </summary>
/// <param name="reader">The reader which's reading-position to set</param>
/// <param name="pos">The reading-position to set</param>
KINC_FUNC void kinc_file_reader_seek(kinc_file_reader_t *reader, int pos);
/// <summary>
/// Interprets four bytes starting at the provided pointer as a little-endian float.
/// </summary>
KINC_FUNC float kinc_read_f32le(uint8_t *data);
/// <summary>
/// Interprets four bytes starting at the provided pointer as a big-endian float.
/// </summary>
KINC_FUNC float kinc_read_f32be(uint8_t *data);
/// <summary>
/// Interprets eight bytes starting at the provided pointer as a little-endian uint64.
/// </summary>
KINC_FUNC uint64_t kinc_read_u64le(uint8_t *data);
/// <summary>
/// Interprets eight bytes starting at the provided pointer as a big-endian uint64.
/// </summary>
KINC_FUNC uint64_t kinc_read_u64be(uint8_t *data);
/// <summary>
/// Interprets eight bytes starting at the provided pointer as a little-endian int64.
/// </summary>
KINC_FUNC int64_t kinc_read_s64le(uint8_t *data);
/// <summary>
/// Interprets eight bytes starting at the provided pointer as a big-endian int64.
/// </summary>
KINC_FUNC int64_t kinc_read_s64be(uint8_t *data);
/// <summary>
/// Interprets four bytes starting at the provided pointer as a little-endian uint32.
/// </summary>
KINC_FUNC uint32_t kinc_read_u32le(uint8_t *data);
/// <summary>
/// Interprets four bytes starting at the provided pointer as a big-endian uint32.
/// </summary>
KINC_FUNC uint32_t kinc_read_u32be(uint8_t *data);
/// <summary>
/// Interprets four bytes starting at the provided pointer as a little-endian int32.
/// </summary>
KINC_FUNC int32_t kinc_read_s32le(uint8_t *data);
/// <summary>
/// Interprets four bytes starting at the provided pointer as a big-endian int32.
/// </summary>
KINC_FUNC int32_t kinc_read_s32be(uint8_t *data);
/// <summary>
/// Interprets two bytes starting at the provided pointer as a little-endian uint16.
/// </summary>
KINC_FUNC uint16_t kinc_read_u16le(uint8_t *data);
/// <summary>
/// Interprets two bytes starting at the provided pointer as a big-endian uint16.
/// </summary>
KINC_FUNC uint16_t kinc_read_u16be(uint8_t *data);
/// <summary>
/// Interprets two bytes starting at the provided pointer as a little-endian int16.
/// </summary>
KINC_FUNC int16_t kinc_read_s16le(uint8_t *data);
/// <summary>
/// Interprets two bytes starting at the provided pointer as a big-endian int16.
/// </summary>
KINC_FUNC int16_t kinc_read_s16be(uint8_t *data);
/// <summary>
/// Interprets one byte starting at the provided pointer as a uint8.
/// </summary>
KINC_FUNC uint8_t kinc_read_u8(uint8_t *data);
/// <summary>
/// Interprets one byte starting at the provided pointer as an int8.
/// </summary>
KINC_FUNC int8_t kinc_read_s8(uint8_t *data);
void kinc_internal_set_files_location(char *dir);
char *kinc_internal_get_files_location(void);
#ifdef KINC_IMPLEMENTATION_IO
#define KINC_IMPLEMENTATION
#endif
#ifdef KINC_IMPLEMENTATION
#include "filereader.h"
#undef KINC_IMPLEMENTATION
#include <kinc/system.h>
#define KINC_IMPLEMENTATION
#ifdef KORE_ANDROID
#include <kinc/backend/Android.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef KORE_WINDOWS
#include <malloc.h>
#include <memory.h>
#endif
#ifndef KORE_CONSOLE
#ifdef KORE_IOS
const char *iphonegetresourcepath(void);
#endif
#ifdef KORE_MACOS
const char *macgetresourcepath(void);
#endif
#if defined(KORE_WINDOWS) || defined(KORE_WINDOWSAPP)
#include <kinc/backend/MiniWindows.h>
#endif
#ifdef KORE_TIZEN
#include <FApp.h>
#endif
#ifdef KORE_PI
#define KORE_LINUX
#endif
static char *fileslocation = NULL;
#ifdef KORE_WINDOWS
static wchar_t wfilepath[1001];
#endif
void kinc_internal_set_files_location(char *dir) {
fileslocation = dir;
}
char *kinc_internal_get_files_location(void) {
return fileslocation;
}
#ifdef KORE_WINDOWSAPP
void kinc_internal_uwp_installed_location_path(char *path);
#endif
#ifndef KORE_ANDROID
bool kinc_file_reader_open(kinc_file_reader_t *reader, const char *filename, int type) {
memset(reader, 0, sizeof(kinc_file_reader_t));
char filepath[1001];
#ifdef KORE_IOS
strcpy(filepath, type == KINC_FILE_TYPE_SAVE ? kinc_internal_save_path() : iphonegetresourcepath());
if (type != KINC_FILE_TYPE_SAVE) {
strcat(filepath, "/");
strcat(filepath, KORE_DEBUGDIR);
strcat(filepath, "/");
}
strcat(filepath, filename);
#endif
#ifdef KORE_MACOS
strcpy(filepath, type == KINC_FILE_TYPE_SAVE ? kinc_internal_save_path() : macgetresourcepath());
if (type != KINC_FILE_TYPE_SAVE) {
strcat(filepath, "/");
strcat(filepath, KORE_DEBUGDIR);
strcat(filepath, "/");
}
strcat(filepath, filename);
#endif
#ifdef KORE_WINDOWS
if (type == KINC_FILE_TYPE_SAVE) {
strcpy(filepath, kinc_internal_save_path());
strcat(filepath, filename);
}
else {
strcpy(filepath, filename);
}
size_t filepathlength = strlen(filepath);
for (size_t i = 0; i < filepathlength; ++i)
if (filepath[i] == '/')
filepath[i] = '\\';
#endif
#ifdef KORE_WINDOWSAPP
kinc_internal_uwp_installed_location_path(filepath);
strcat(filepath, "\\");
strcat(filepath, filename);
#endif
#ifdef KORE_LINUX
if (type == KINC_FILE_TYPE_SAVE) {
strcpy(filepath, kinc_internal_save_path());
strcat(filepath, filename);
}
else {
strcpy(filepath, filename);
}
#endif
#ifdef KORE_WASM
strcpy(filepath, filename);
#endif
#ifdef KORE_EMSCRIPTEN
strcpy(filepath, KORE_DEBUGDIR);
strcat(filepath, "/");
strcat(filepath, filename);
#endif
#ifdef KORE_TIZEN
for (int i = 0; i < Tizen::App::App::GetInstance()->GetAppDataPath().GetLength(); ++i) {
wchar_t c;
Tizen::App::App::GetInstance()->GetAppDataPath().GetCharAt(i, c);
filepath[i] = (char)c;
}
filepath[Tizen::App::App::GetInstance()->GetAppDataPath().GetLength()] = 0;
strcat(filepath, "/");
strcat(filepath, filename);
#endif
#ifdef KORE_WINDOWS
// Drive letter or network
bool isAbsolute = (filename[1] == ':' && filename[2] == '\\') || (filename[0] == '\\' && filename[1] == '\\');
#else
bool isAbsolute = filename[0] == '/';
#endif
if (isAbsolute) {
strcpy(filepath, filename);
}
else if (fileslocation != NULL && type != KINC_FILE_TYPE_SAVE) {
strcpy(filepath, fileslocation);
strcat(filepath, "/");
strcat(filepath, filename);
}
#ifdef KORE_WINDOWS
MultiByteToWideChar(CP_UTF8, 0, filepath, -1, wfilepath, 1000);
reader->file = CreateFileW(wfilepath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (reader->file == INVALID_HANDLE_VALUE) {
return false;
}
#else
reader->file = fopen(filepath, "rb");
if (reader->file == NULL) {
return false;
}
#endif
#ifdef KORE_WINDOWS
reader->size = GetFileSize(reader->file, NULL);
#else
fseek((FILE *)reader->file, 0, SEEK_END);
reader->size = (int)ftell((FILE *)reader->file);
fseek((FILE *)reader->file, 0, SEEK_SET);
#endif
return true;
}
#endif
int kinc_file_reader_read(kinc_file_reader_t *reader, void *data, size_t size) {
#ifdef KORE_ANDROID
if (reader->file != NULL) {
return (int)fread(data, 1, size, reader->file);
}
else {
int read = AAsset_read(reader->asset, data, size);
reader->pos += read;
return read;
}
#elif defined(KORE_WINDOWS)
DWORD readBytes = 0;
if (ReadFile(reader->file, data, (DWORD)size, &readBytes, NULL)) {
return (int)readBytes;
}
else {
return 0;
}
#else
return (int)fread(data, 1, size, (FILE *)reader->file);
#endif
}
void kinc_file_reader_seek(kinc_file_reader_t *reader, int pos) {
#ifdef KORE_ANDROID
if (reader->file != NULL) {
fseek(reader->file, pos, SEEK_SET);
}
else {
AAsset_seek(reader->asset, pos, SEEK_SET);
reader->pos = pos;
}
#elif defined(KORE_WINDOWS)
SetFilePointer(reader->file, pos, NULL, FILE_BEGIN);
#else
fseek((FILE *)reader->file, pos, SEEK_SET);
#endif
}
void kinc_file_reader_close(kinc_file_reader_t *reader) {
#ifdef KORE_ANDROID
if (reader->file != NULL) {
fclose(reader->file);
reader->file = NULL;
}
if (reader->asset != NULL) {
AAsset_close(reader->asset);
reader->asset = NULL;
}
#elif defined(KORE_WINDOWS)
CloseHandle(reader->file);
#else
if (reader->file == NULL) {
return;
}
fclose((FILE *)reader->file);
reader->file = NULL;
#endif
}
int kinc_file_reader_pos(kinc_file_reader_t *reader) {
#ifdef KORE_ANDROID
if (reader->file != NULL)
return (int)ftell(reader->file);
else
return reader->pos;
#elif defined(KORE_WINDOWS)
return (int)SetFilePointer(reader->file, 0, NULL, FILE_CURRENT);
#else
return (int)ftell((FILE *)reader->file);
#endif
}
size_t kinc_file_reader_size(kinc_file_reader_t *reader) {
return (size_t)reader->size;
}
#endif
float kinc_read_f32le(uint8_t *data) {
#ifdef KORE_LITTLE_ENDIAN // speed optimization
return *(float *)data;
#else // works on all architectures
int i = (data[0] << 0) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
return *(float *)&i;
#endif
}
float kinc_read_f32be(uint8_t *data) {
#ifdef KORE_BIG_ENDIAN // speed optimization
return *(float *)data;
#else // works on all architectures
int i = (data[3] << 0) | (data[2] << 8) | (data[1] << 16) | (data[0] << 24);
return *(float *)&i;
#endif
}
uint64_t kinc_read_u64le(uint8_t *data) {
#ifdef KORE_LITTLE_ENDIAN
return *(uint64_t *)data;
#else
return ((uint64_t)data[0] << 0) | ((uint64_t)data[1] << 8) | ((uint64_t)data[2] << 16) | ((uint64_t)data[3] << 24) | ((uint64_t)data[4] << 32) |
((uint64_t)data[5] << 40) | ((uint64_t)data[6] << 48) | ((uint64_t)data[7] << 56);
#endif
}
uint64_t kinc_read_u64be(uint8_t *data) {
#ifdef KORE_BIG_ENDIAN
return *(uint64_t *)data;
#else
return ((uint64_t)data[7] << 0) | ((uint64_t)data[6] << 8) | ((uint64_t)data[5] << 16) | ((uint64_t)data[4] << 24) | ((uint64_t)data[3] << 32) |
((uint64_t)data[2] << 40) | ((uint64_t)data[1] << 48) | ((uint64_t)data[0] << 56);
#endif
}
int64_t kinc_read_s64le(uint8_t *data) {
#ifdef KORE_LITTLE_ENDIAN
return *(int64_t *)data;
#else
return ((int64_t)data[0] << 0) | ((int64_t)data[1] << 8) | ((int64_t)data[2] << 16) | ((int64_t)data[3] << 24) | ((int64_t)data[4] << 32) |
((int64_t)data[5] << 40) | ((int64_t)data[6] << 48) | ((int64_t)data[7] << 56);
#endif
}
int64_t kinc_read_s64be(uint8_t *data) {
#ifdef KORE_BIG_ENDIAN
return *(int64_t *)data;
#else
return ((int64_t)data[7] << 0) | ((int64_t)data[6] << 8) | ((int64_t)data[5] << 16) | ((int64_t)data[4] << 24) | ((int64_t)data[3] << 32) |
((int64_t)data[2] << 40) | ((int64_t)data[1] << 48) | ((int64_t)data[0] << 56);
#endif
}
uint32_t kinc_read_u32le(uint8_t *data) {
#ifdef KORE_LITTLE_ENDIAN
return *(uint32_t *)data;
#else
return (data[0] << 0) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
#endif
}
uint32_t kinc_read_u32be(uint8_t *data) {
#ifdef KORE_BIG_ENDIAN
return *(uint32_t *)data;
#else
return (data[3] << 0) | (data[2] << 8) | (data[1] << 16) | (data[0] << 24);
#endif
}
int32_t kinc_read_s32le(uint8_t *data) {
#ifdef KORE_LITTLE_ENDIAN
return *(int32_t *)data;
#else
return (data[0] << 0) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
#endif
}
int32_t kinc_read_s32be(uint8_t *data) {
#ifdef KORE_BIG_ENDIAN
return *(int32_t *)data;
#else
return (data[3] << 0) | (data[2] << 8) | (data[1] << 16) | (data[0] << 24);
#endif
}
uint16_t kinc_read_u16le(uint8_t *data) {
#ifdef KORE_LITTLE_ENDIAN
return *(uint16_t *)data;
#else
return (data[0] << 0) | (data[1] << 8);
#endif
}
uint16_t kinc_read_u16be(uint8_t *data) {
#ifdef KORE_BIG_ENDIAN
return *(uint16_t *)data;
#else
return (data[1] << 0) | (data[0] << 8);
#endif
}
int16_t kinc_read_s16le(uint8_t *data) {
#ifdef KORE_LITTLE_ENDIAN
return *(int16_t *)data;
#else
return (data[0] << 0) | (data[1] << 8);
#endif
}
int16_t kinc_read_s16be(uint8_t *data) {
#ifdef KORE_BIG_ENDIAN
return *(int16_t *)data;
#else
return (data[1] << 0) | (data[0] << 8);
#endif
}
uint8_t kinc_read_u8(uint8_t *data) {
return *data;
}
int8_t kinc_read_s8(uint8_t *data) {
return *(int8_t *)data;
}
#endif
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,132 @@
#pragma once
#include <kinc/global.h>
#include <stdbool.h>
/*! \file filewriter.h
\brief Provides an API very similar to fwrite and friends but uses a directory that can actually used for persistent file storage. This can later be read
using the kinc_file_reader-functions and KINC_FILE_TYPE_SAVE.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef struct kinc_file_writer {
void *file;
const char *filename;
bool mounted;
} kinc_file_writer_t;
/// <summary>
/// Opens a file for writing.
/// </summary>
/// <param name="reader">The writer to initialize for writing</param>
/// <param name="filepath">A filepath to identify a file</param>
/// <returns>Whether the file could be opened</returns>
KINC_FUNC bool kinc_file_writer_open(kinc_file_writer_t *writer, const char *filepath);
/// <summary>
/// Writes data to a file starting from the current writing-position and increases the writing-position accordingly.
/// </summary>
/// <param name="reader">The writer to write to</param>
/// <param name="data">A pointer to read the data from</param>
/// <param name="size">The amount of data to write in bytes</param>
KINC_FUNC void kinc_file_writer_write(kinc_file_writer_t *writer, void *data, int size);
/// <summary>
/// Closes a file.
/// </summary>
/// <param name="reader">The file to close</param>
KINC_FUNC void kinc_file_writer_close(kinc_file_writer_t *writer);
#ifdef KINC_IMPLEMENTATION_IO
#define KINC_IMPLEMENTATION
#endif
#ifdef KINC_IMPLEMENTATION
#if !defined(KORE_CONSOLE)
#include "filewriter.h"
#undef KINC_IMPLEMENTATION
#include <kinc/error.h>
#include <kinc/log.h>
#include <kinc/system.h>
#define KINC_IMPLEMENTATION
#include <stdio.h>
#include <string.h>
#if defined(KORE_WINDOWS)
#include <kinc/backend/MiniWindows.h>
#endif
#if defined(KORE_PS4) || defined(KORE_SWITCH)
#define MOUNT_SAVES
bool mountSaveData(bool);
void unmountSaveData();
#endif
bool kinc_file_writer_open(kinc_file_writer_t *writer, const char *filepath) {
writer->file = NULL;
writer->mounted = false;
#ifdef MOUNT_SAVES
if (!mountSaveData(true)) {
return false;
}
writer->mounted = true;
#endif
char path[1001];
strcpy(path, kinc_internal_save_path());
strcat(path, filepath);
#ifdef KORE_WINDOWS
wchar_t wpath[MAX_PATH];
MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, MAX_PATH);
writer->file = CreateFileW(wpath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
#else
writer->file = fopen(path, "wb");
#endif
if (writer->file == NULL) {
kinc_log(KINC_LOG_LEVEL_WARNING, "Could not open file %s.", filepath);
return false;
}
return true;
}
void kinc_file_writer_close(kinc_file_writer_t *writer) {
if (writer->file != NULL) {
#ifdef KORE_WINDOWS
CloseHandle(writer->file);
#else
fclose((FILE *)writer->file);
#endif
writer->file = NULL;
}
#ifdef MOUNT_SAVES
if (writer->mounted) {
writer->mounted = false;
unmountSaveData();
}
#endif
}
void kinc_file_writer_write(kinc_file_writer_t *writer, void *data, int size) {
#ifdef KORE_WINDOWS
DWORD written = 0;
WriteFile(writer->file, data, (DWORD)size, &written, NULL);
#else
fwrite(data, 1, size, (FILE *)writer->file);
#endif
}
#endif
#endif
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,4 @@
#define KINC_IMPLEMENTATION_IO
#include "filereader.h"
#include "filewriter.h"

View File

@ -0,0 +1,24 @@
LZ4 Library
Copyright (c) 2011-2016, Yann Collet
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,463 @@
/*
* LZ4 - Fast LZ compression algorithm
* Header File
* Copyright (C) 2011-2016, Yann Collet.
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
You can contact the author at :
- LZ4 homepage : http://www.lz4.org
- LZ4 source repository : https://github.com/lz4/lz4
*/
#ifndef LZ4_H_2983827168210
#define LZ4_H_2983827168210
#if defined (__cplusplus)
extern "C" {
#endif
/* --- Dependency --- */
#include <stddef.h> /* size_t */
/**
Introduction
LZ4 is lossless compression algorithm, providing compression speed at 400 MB/s per core,
scalable with multi-cores CPU. It features an extremely fast decoder, with speed in
multiple GB/s per core, typically reaching RAM speed limits on multi-core systems.
The LZ4 compression library provides in-memory compression and decompression functions.
Compression can be done in:
- a single step (described as Simple Functions)
- a single step, reusing a context (described in Advanced Functions)
- unbounded multiple steps (described as Streaming compression)
lz4.h provides block compression functions. It gives full buffer control to user.
Decompressing an lz4-compressed block also requires metadata (such as compressed size).
Each application is free to encode such metadata in whichever way it wants.
An additional format, called LZ4 frame specification (doc/lz4_Frame_format.md),
take care of encoding standard metadata alongside LZ4-compressed blocks.
If your application requires interoperability, it's recommended to use it.
A library is provided to take care of it, see lz4frame.h.
*/
/*^***************************************************************
* Export parameters
*****************************************************************/
/*
* LZ4_DLL_EXPORT :
* Enable exporting of functions when building a Windows DLL
*/
#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1)
# define LZ4LIB_API __declspec(dllexport)
#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1)
# define LZ4LIB_API __declspec(dllimport) /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
#else
# define LZ4LIB_API
#endif
/*========== Version =========== */
#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */
#define LZ4_VERSION_MINOR 7 /* for new (non-breaking) interface capabilities */
#define LZ4_VERSION_RELEASE 5 /* for tweaks, bug-fixes, or development */
#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)
#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE
#define LZ4_QUOTE(str) #str
#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str)
#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION)
LZ4LIB_API int LZ4_versionNumber (void);
LZ4LIB_API const char* LZ4_versionString (void);
/*-************************************
* Tuning parameter
**************************************/
/*!
* LZ4_MEMORY_USAGE :
* Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.)
* Increasing memory usage improves compression ratio
* Reduced memory usage can improve speed, due to cache effect
* Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache
*/
#define LZ4_MEMORY_USAGE 14
/*-************************************
* Simple Functions
**************************************/
/*! LZ4_compress_default() :
Compresses 'sourceSize' bytes from buffer 'source'
into already allocated 'dest' buffer of size 'maxDestSize'.
Compression is guaranteed to succeed if 'maxDestSize' >= LZ4_compressBound(sourceSize).
It also runs faster, so it's a recommended setting.
If the function cannot compress 'source' into a more limited 'dest' budget,
compression stops *immediately*, and the function result is zero.
As a consequence, 'dest' content is not valid.
This function never writes outside 'dest' buffer, nor read outside 'source' buffer.
sourceSize : Max supported value is LZ4_MAX_INPUT_VALUE
maxDestSize : full or partial size of buffer 'dest' (which must be already allocated)
return : the number of bytes written into buffer 'dest' (necessarily <= maxOutputSize)
or 0 if compression fails */
LZ4LIB_API int LZ4_compress_default(const char* source, char* dest, int sourceSize, int maxDestSize);
/*! LZ4_decompress_safe() :
compressedSize : is the precise full size of the compressed block.
maxDecompressedSize : is the size of destination buffer, which must be already allocated.
return : the number of bytes decompressed into destination buffer (necessarily <= maxDecompressedSize)
If destination buffer is not large enough, decoding will stop and output an error code (<0).
If the source stream is detected malformed, the function will stop decoding and return a negative result.
This function is protected against buffer overflow exploits, including malicious data packets.
It never writes outside output buffer, nor reads outside input buffer.
*/
LZ4LIB_API int LZ4_decompress_safe (const char* source, char* dest, int compressedSize, int maxDecompressedSize);
/*-************************************
* Advanced Functions
**************************************/
#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */
#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)
/*!
LZ4_compressBound() :
Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible)
This function is primarily useful for memory allocation purposes (destination buffer size).
Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example).
Note that LZ4_compress_default() compress faster when dest buffer size is >= LZ4_compressBound(srcSize)
inputSize : max supported value is LZ4_MAX_INPUT_SIZE
return : maximum output size in a "worst case" scenario
or 0, if input size is too large ( > LZ4_MAX_INPUT_SIZE)
*/
LZ4LIB_API int LZ4_compressBound(int inputSize);
/*!
LZ4_compress_fast() :
Same as LZ4_compress_default(), but allows to select an "acceleration" factor.
The larger the acceleration value, the faster the algorithm, but also the lesser the compression.
It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed.
An acceleration value of "1" is the same as regular LZ4_compress_default()
Values <= 0 will be replaced by ACCELERATION_DEFAULT (see lz4.c), which is 1.
*/
LZ4LIB_API int LZ4_compress_fast (const char* source, char* dest, int sourceSize, int maxDestSize, int acceleration);
/*!
LZ4_compress_fast_extState() :
Same compression function, just using an externally allocated memory space to store compression state.
Use LZ4_sizeofState() to know how much memory must be allocated,
and allocate it on 8-bytes boundaries (using malloc() typically).
Then, provide it as 'void* state' to compression function.
*/
LZ4LIB_API int LZ4_sizeofState(void);
LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* source, char* dest, int inputSize, int maxDestSize, int acceleration);
/*!
LZ4_compress_destSize() :
Reverse the logic, by compressing as much data as possible from 'source' buffer
into already allocated buffer 'dest' of size 'targetDestSize'.
This function either compresses the entire 'source' content into 'dest' if it's large enough,
or fill 'dest' buffer completely with as much data as possible from 'source'.
*sourceSizePtr : will be modified to indicate how many bytes where read from 'source' to fill 'dest'.
New value is necessarily <= old value.
return : Nb bytes written into 'dest' (necessarily <= targetDestSize)
or 0 if compression fails
*/
LZ4LIB_API int LZ4_compress_destSize (const char* source, char* dest, int* sourceSizePtr, int targetDestSize);
/*!
LZ4_decompress_fast() :
originalSize : is the original and therefore uncompressed size
return : the number of bytes read from the source buffer (in other words, the compressed size)
If the source stream is detected malformed, the function will stop decoding and return a negative result.
Destination buffer must be already allocated. Its size must be a minimum of 'originalSize' bytes.
note : This function fully respect memory boundaries for properly formed compressed data.
It is a bit faster than LZ4_decompress_safe().
However, it does not provide any protection against intentionally modified data stream (malicious input).
Use this function in trusted environment only (data to decode comes from a trusted source).
*/
LZ4LIB_API int LZ4_decompress_fast (const char* source, char* dest, int originalSize);
/*!
LZ4_decompress_safe_partial() :
This function decompress a compressed block of size 'compressedSize' at position 'source'
into destination buffer 'dest' of size 'maxDecompressedSize'.
The function tries to stop decompressing operation as soon as 'targetOutputSize' has been reached,
reducing decompression time.
return : the number of bytes decoded in the destination buffer (necessarily <= maxDecompressedSize)
Note : this number can be < 'targetOutputSize' should the compressed block to decode be smaller.
Always control how many bytes were decoded.
If the source stream is detected malformed, the function will stop decoding and return a negative result.
This function never writes outside of output buffer, and never reads outside of input buffer. It is therefore protected against malicious data packets
*/
LZ4LIB_API int LZ4_decompress_safe_partial (const char* source, char* dest, int compressedSize, int targetOutputSize, int maxDecompressedSize);
/*-*********************************************
* Streaming Compression Functions
***********************************************/
typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */
/*! LZ4_createStream() and LZ4_freeStream() :
* LZ4_createStream() will allocate and initialize an `LZ4_stream_t` structure.
* LZ4_freeStream() releases its memory.
*/
LZ4LIB_API LZ4_stream_t* LZ4_createStream(void);
LZ4LIB_API int LZ4_freeStream (LZ4_stream_t* streamPtr);
/*! LZ4_resetStream() :
* An LZ4_stream_t structure can be allocated once and re-used multiple times.
* Use this function to init an allocated `LZ4_stream_t` structure and start a new compression.
*/
LZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr);
/*! LZ4_loadDict() :
* Use this function to load a static dictionary into LZ4_stream.
* Any previous data will be forgotten, only 'dictionary' will remain in memory.
* Loading a size of 0 is allowed.
* Return : dictionary size, in bytes (necessarily <= 64 KB)
*/
LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize);
/*! LZ4_compress_fast_continue() :
* Compress buffer content 'src', using data from previously compressed blocks as dictionary to improve compression ratio.
* Important : Previous data blocks are assumed to still be present and unmodified !
* 'dst' buffer must be already allocated.
* If maxDstSize >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster.
* If not, and if compressed data cannot fit into 'dst' buffer size, compression stops, and function returns a zero.
*/
LZ4LIB_API int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int maxDstSize, int acceleration);
/*! LZ4_saveDict() :
* If previously compressed data block is not guaranteed to remain available at its memory location,
* save it into a safer place (char* safeBuffer).
* Note : you don't need to call LZ4_loadDict() afterwards,
* dictionary is immediately usable, you can therefore call LZ4_compress_fast_continue().
* Return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error.
*/
LZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int dictSize);
/*-**********************************************
* Streaming Decompression Functions
* Bufferless synchronous API
************************************************/
typedef union LZ4_streamDecode_u LZ4_streamDecode_t; /* incomplete type (defined later) */
/* creation / destruction of streaming decompression tracking structure */
LZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void);
LZ4LIB_API int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream);
/*! LZ4_setStreamDecode() :
* Use this function to instruct where to find the dictionary.
* Setting a size of 0 is allowed (same effect as reset).
* @return : 1 if OK, 0 if error
*/
LZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize);
/*!
LZ4_decompress_*_continue() :
These decoding functions allow decompression of multiple blocks in "streaming" mode.
Previously decoded blocks *must* remain available at the memory position where they were decoded (up to 64 KB)
In the case of a ring buffers, decoding buffer must be either :
- Exactly same size as encoding buffer, with same update rule (block boundaries at same positions)
In which case, the decoding & encoding ring buffer can have any size, including very small ones ( < 64 KB).
- Larger than encoding buffer, by a minimum of maxBlockSize more bytes.
maxBlockSize is implementation dependent. It's the maximum size you intend to compress into a single block.
In which case, encoding and decoding buffers do not need to be synchronized,
and encoding ring buffer can have any size, including small ones ( < 64 KB).
- _At least_ 64 KB + 8 bytes + maxBlockSize.
In which case, encoding and decoding buffers do not need to be synchronized,
and encoding ring buffer can have any size, including larger than decoding buffer.
Whenever these conditions are not possible, save the last 64KB of decoded data into a safe buffer,
and indicate where it is saved using LZ4_setStreamDecode()
*/
LZ4LIB_API int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxDecompressedSize);
LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize);
/*! LZ4_decompress_*_usingDict() :
* These decoding functions work the same as
* a combination of LZ4_setStreamDecode() followed by LZ4_decompress_*_continue()
* They are stand-alone, and don't need an LZ4_streamDecode_t structure.
*/
LZ4LIB_API int LZ4_decompress_safe_usingDict (const char* source, char* dest, int compressedSize, int maxDecompressedSize, const char* dictStart, int dictSize);
LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* source, char* dest, int originalSize, const char* dictStart, int dictSize);
/*^**********************************************
* !!!!!! STATIC LINKING ONLY !!!!!!
***********************************************/
/*-************************************
* Private definitions
**************************************
* Do not use these definitions.
* They are exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`.
* Using these definitions will expose code to API and/or ABI break in future versions of the library.
**************************************/
#define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2)
#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE)
#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */
#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
#include <stdint.h>
typedef struct {
uint32_t hashTable[LZ4_HASH_SIZE_U32];
uint32_t currentOffset;
uint32_t initCheck;
const uint8_t* dictionary;
uint8_t* bufferStart; /* obsolete, used for slideInputBuffer */
uint32_t dictSize;
} LZ4_stream_t_internal;
typedef struct {
const uint8_t* externalDict;
size_t extDictSize;
const uint8_t* prefixEnd;
size_t prefixSize;
} LZ4_streamDecode_t_internal;
#else
typedef struct {
unsigned int hashTable[LZ4_HASH_SIZE_U32];
unsigned int currentOffset;
unsigned int initCheck;
const unsigned char* dictionary;
unsigned char* bufferStart; /* obsolete, used for slideInputBuffer */
unsigned int dictSize;
} LZ4_stream_t_internal;
typedef struct {
const unsigned char* externalDict;
size_t extDictSize;
const unsigned char* prefixEnd;
size_t prefixSize;
} LZ4_streamDecode_t_internal;
#endif
/*!
* LZ4_stream_t :
* information structure to track an LZ4 stream.
* init this structure before first use.
* note : only use in association with static linking !
* this definition is not API/ABI safe,
* and may change in a future version !
*/
#define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4)
#define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(unsigned long long))
union LZ4_stream_u {
unsigned long long table[LZ4_STREAMSIZE_U64];
LZ4_stream_t_internal internal_donotuse;
} ; /* previously typedef'd to LZ4_stream_t */
/*!
* LZ4_streamDecode_t :
* information structure to track an LZ4 stream during decompression.
* init this structure using LZ4_setStreamDecode (or memset()) before first use
* note : only use in association with static linking !
* this definition is not API/ABI safe,
* and may change in a future version !
*/
#define LZ4_STREAMDECODESIZE_U64 4
#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long))
union LZ4_streamDecode_u {
unsigned long long table[LZ4_STREAMDECODESIZE_U64];
LZ4_streamDecode_t_internal internal_donotuse;
} ; /* previously typedef'd to LZ4_streamDecode_t */
/*=************************************
* Obsolete Functions
**************************************/
/* Deprecation warnings */
/* Should these warnings be a problem,
it is generally possible to disable them,
typically with -Wno-deprecated-declarations for gcc
or _CRT_SECURE_NO_WARNINGS in Visual.
Otherwise, it's also possible to define LZ4_DISABLE_DEPRECATE_WARNINGS */
#ifdef LZ4_DISABLE_DEPRECATE_WARNINGS
# define LZ4_DEPRECATED(message) /* disable deprecation warnings */
#else
# define LZ4_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */
# define LZ4_DEPRECATED(message) [[deprecated(message)]]
# elif (LZ4_GCC_VERSION >= 405) || defined(__clang__)
# define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))
# elif (LZ4_GCC_VERSION >= 301)
# define LZ4_DEPRECATED(message) __attribute__((deprecated))
# elif defined(_MSC_VER)
# define LZ4_DEPRECATED(message) __declspec(deprecated(message))
# else
# pragma message("WARNING: You need to implement LZ4_DEPRECATED for this compiler")
# define LZ4_DEPRECATED(message)
# endif
#endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */
/* Obsolete compression functions */
LZ4_DEPRECATED("use LZ4_compress_default() instead") int LZ4_compress (const char* source, char* dest, int sourceSize);
LZ4_DEPRECATED("use LZ4_compress_default() instead") int LZ4_compress_limitedOutput (const char* source, char* dest, int sourceSize, int maxOutputSize);
LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize);
LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);
LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize);
LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize);
/* Obsolete decompression functions */
/* These function names are completely deprecated and must no longer be used.
They are only provided in lz4.c for compatibility with older programs.
- LZ4_uncompress is the same as LZ4_decompress_fast
- LZ4_uncompress_unknownOutputSize is the same as LZ4_decompress_safe
These function prototypes are now disabled; uncomment them only if you really need them.
It is highly recommended to stop using these prototypes and migrate to maintained ones */
/* int LZ4_uncompress (const char* source, char* dest, int outputSize); */
/* int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); */
/* Obsolete streaming functions; use new streaming interface whenever possible */
LZ4_DEPRECATED("use LZ4_createStream() instead") void* LZ4_create (char* inputBuffer);
LZ4_DEPRECATED("use LZ4_createStream() instead") int LZ4_sizeofStreamState(void);
LZ4_DEPRECATED("use LZ4_resetStream() instead") int LZ4_resetStreamState(void* state, char* inputBuffer);
LZ4_DEPRECATED("use LZ4_saveDict() instead") char* LZ4_slideInputBuffer (void* state);
/* Obsolete streaming decoding functions */
LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize);
LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize);
#if defined (__cplusplus)
}
#endif
#endif /* LZ4_H_2983827168210 */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,340 @@
/*
LZ4 auto-framing library
Header File
Copyright (C) 2011-2016, Yann Collet.
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
You can contact the author at :
- LZ4 source repository : https://github.com/lz4/lz4
- LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
*/
/* LZ4F is a stand-alone API to create LZ4-compressed frames
* conformant with specification v1.5.1.
* It also offers streaming capabilities.
* lz4.h is not required when using lz4frame.h.
* */
#ifndef LZ4F_H_09782039843
#define LZ4F_H_09782039843
#if defined (__cplusplus)
extern "C" {
#endif
/* --- Dependency --- */
#include <stddef.h> /* size_t */
/*-***************************************************************
* Compiler specifics
*****************************************************************/
/*!
* LZ4_DLL_EXPORT :
* Enable exporting of functions when building a Windows DLL
*/
#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1)
# define LZ4FLIB_API __declspec(dllexport)
#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1)
# define LZ4FLIB_API __declspec(dllimport)
#else
# define LZ4FLIB_API
#endif
#if defined(_MSC_VER)
# define LZ4F_DEPRECATE(x) x /* __declspec(deprecated) x - only works with C++ */
#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 6))
# define LZ4F_DEPRECATE(x) x __attribute__((deprecated))
#else
# define LZ4F_DEPRECATE(x) x /* no deprecation warning for this compiler */
#endif
/*-************************************
* Error management
**************************************/
typedef size_t LZ4F_errorCode_t;
LZ4FLIB_API unsigned LZ4F_isError(LZ4F_errorCode_t code);
LZ4FLIB_API const char* LZ4F_getErrorName(LZ4F_errorCode_t code); /* return error code string; useful for debugging */
/*-************************************
* Frame compression types
**************************************/
/* #define LZ4F_DISABLE_OBSOLETE_ENUMS */ /* uncomment to disable obsolete enums */
#ifndef LZ4F_DISABLE_OBSOLETE_ENUMS
# define LZ4F_OBSOLETE_ENUM(x) , LZ4F_DEPRECATE(x) = LZ4F_##x
#else
# define LZ4F_OBSOLETE_ENUM(x)
#endif
typedef enum {
LZ4F_default=0,
LZ4F_max64KB=4,
LZ4F_max256KB=5,
LZ4F_max1MB=6,
LZ4F_max4MB=7
LZ4F_OBSOLETE_ENUM(max64KB)
LZ4F_OBSOLETE_ENUM(max256KB)
LZ4F_OBSOLETE_ENUM(max1MB)
LZ4F_OBSOLETE_ENUM(max4MB)
} LZ4F_blockSizeID_t;
typedef enum {
LZ4F_blockLinked=0,
LZ4F_blockIndependent
LZ4F_OBSOLETE_ENUM(blockLinked)
LZ4F_OBSOLETE_ENUM(blockIndependent)
} LZ4F_blockMode_t;
typedef enum {
LZ4F_noContentChecksum=0,
LZ4F_contentChecksumEnabled
LZ4F_OBSOLETE_ENUM(noContentChecksum)
LZ4F_OBSOLETE_ENUM(contentChecksumEnabled)
} LZ4F_contentChecksum_t;
typedef enum {
LZ4F_frame=0,
LZ4F_skippableFrame
LZ4F_OBSOLETE_ENUM(skippableFrame)
} LZ4F_frameType_t;
#ifndef LZ4F_DISABLE_OBSOLETE_ENUMS
typedef LZ4F_blockSizeID_t blockSizeID_t;
typedef LZ4F_blockMode_t blockMode_t;
typedef LZ4F_frameType_t frameType_t;
typedef LZ4F_contentChecksum_t contentChecksum_t;
#endif
/* LZ4F_frameInfo_t :
* makes it possible to supply detailed frame parameters to the stream interface.
* It's not required to set all fields, as long as the structure was initially memset() to zero.
* All reserved fields must be set to zero. */
typedef struct {
LZ4F_blockSizeID_t blockSizeID; /* max64KB, max256KB, max1MB, max4MB ; 0 == default */
LZ4F_blockMode_t blockMode; /* blockLinked, blockIndependent ; 0 == default */
LZ4F_contentChecksum_t contentChecksumFlag; /* noContentChecksum, contentChecksumEnabled ; 0 == default */
LZ4F_frameType_t frameType; /* LZ4F_frame, skippableFrame ; 0 == default */
unsigned long long contentSize; /* Size of uncompressed (original) content ; 0 == unknown */
unsigned reserved[2]; /* must be zero for forward compatibility */
} LZ4F_frameInfo_t;
/* LZ4F_preferences_t :
* makes it possible to supply detailed compression parameters to the stream interface.
* It's not required to set all fields, as long as the structure was initially memset() to zero.
* All reserved fields must be set to zero. */
typedef struct {
LZ4F_frameInfo_t frameInfo;
int compressionLevel; /* 0 == default (fast mode); values above 16 count as 16; values below 0 count as 0 */
unsigned autoFlush; /* 1 == always flush (reduce usage of tmp buffer) */
unsigned reserved[4]; /* must be zero for forward compatibility */
} LZ4F_preferences_t;
/*-*********************************
* Simple compression function
***********************************/
/*!LZ4F_compressFrameBound() :
* Returns the maximum possible size of a frame compressed with LZ4F_compressFrame() given srcSize content and preferences.
* Note : this result is only usable with LZ4F_compressFrame(), not with multi-segments compression.
*/
LZ4FLIB_API size_t LZ4F_compressFrameBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr);
/*!LZ4F_compressFrame() :
* Compress an entire srcBuffer into a valid LZ4 frame, as defined by specification v1.5.1
* An important rule is that dstBuffer MUST be large enough (dstCapacity) to store the result in worst case situation.
* This value is supplied by LZ4F_compressFrameBound().
* If this condition is not respected, LZ4F_compressFrame() will fail (result is an errorCode).
* The LZ4F_preferences_t structure is optional : you can provide NULL as argument. All preferences will be set to default.
* @return : number of bytes written into dstBuffer.
* or an error code if it fails (can be tested using LZ4F_isError())
*/
LZ4FLIB_API size_t LZ4F_compressFrame(void* dstBuffer, size_t dstCapacity, const void* srcBuffer, size_t srcSize, const LZ4F_preferences_t* preferencesPtr);
/*-***********************************
* Advanced compression functions
*************************************/
typedef struct LZ4F_cctx_s LZ4F_cctx; /* incomplete type */
typedef LZ4F_cctx* LZ4F_compressionContext_t; /* for compatibility with previous API version */
typedef struct {
unsigned stableSrc; /* 1 == src content will remain present on future calls to LZ4F_compress(); skip copying src content within tmp buffer */
unsigned reserved[3];
} LZ4F_compressOptions_t;
/* Resource Management */
#define LZ4F_VERSION 100
LZ4FLIB_API unsigned LZ4F_getVersion(void);
LZ4FLIB_API LZ4F_errorCode_t LZ4F_createCompressionContext(LZ4F_cctx** cctxPtr, unsigned version);
LZ4FLIB_API LZ4F_errorCode_t LZ4F_freeCompressionContext(LZ4F_cctx* cctx);
/* LZ4F_createCompressionContext() :
* The first thing to do is to create a compressionContext object, which will be used in all compression operations.
* This is achieved using LZ4F_createCompressionContext(), which takes as argument a version and an LZ4F_preferences_t structure.
* The version provided MUST be LZ4F_VERSION. It is intended to track potential version mismatch, notably when using DLL.
* The function will provide a pointer to a fully allocated LZ4F_cctx object.
* If @return != zero, there was an error during context creation.
* Object can release its memory using LZ4F_freeCompressionContext();
*/
/* Compression */
#define LZ4F_HEADER_SIZE_MAX 15
LZ4FLIB_API size_t LZ4F_compressBegin(LZ4F_cctx* cctx, void* dstBuffer, size_t dstCapacity, const LZ4F_preferences_t* prefsPtr);
/* LZ4F_compressBegin() :
* will write the frame header into dstBuffer.
* dstCapacity must be large enough to store the header. Maximum header size is LZ4F_HEADER_SIZE_MAX bytes.
* `prefsPtr` is optional : you can provide NULL as argument, all preferences will then be set to default.
* @return : number of bytes written into dstBuffer for the header
* or an error code (which can be tested using LZ4F_isError())
*/
LZ4FLIB_API size_t LZ4F_compressBound(size_t srcSize, const LZ4F_preferences_t* prefsPtr);
/* LZ4F_compressBound() :
* Provides dstCapacity given a srcSize to guarantee operation success in worst case situations.
* prefsPtr is optional : you can provide NULL as argument, preferences will be set to cover worst case scenario.
* Result is always the same for a srcSize and prefsPtr, so it can be trusted to size reusable buffers.
* When srcSize==0, LZ4F_compressBound() provides an upper bound for LZ4F_flush() and LZ4F_compressEnd() operations.
*/
LZ4FLIB_API size_t LZ4F_compressUpdate(LZ4F_cctx* cctx, void* dstBuffer, size_t dstCapacity, const void* srcBuffer, size_t srcSize, const LZ4F_compressOptions_t* cOptPtr);
/* LZ4F_compressUpdate() :
* LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary.
* An important rule is that dstCapacity MUST be large enough to ensure operation success even in worst case situations.
* This value is provided by LZ4F_compressBound().
* If this condition is not respected, LZ4F_compress() will fail (result is an errorCode).
* LZ4F_compressUpdate() doesn't guarantee error recovery. When an error occurs, compression context must be freed or resized.
* `cOptPtr` is optional : NULL can be provided, in which case all options are set to default.
* @return : number of bytes written into `dstBuffer` (it can be zero, meaning input data was just buffered).
* or an error code if it fails (which can be tested using LZ4F_isError())
*/
LZ4FLIB_API size_t LZ4F_flush(LZ4F_cctx* cctx, void* dstBuffer, size_t dstCapacity, const LZ4F_compressOptions_t* cOptPtr);
/* LZ4F_flush() :
* When data must be generated and sent immediately, without waiting for a block to be completely filled,
* it's possible to call LZ4_flush(). It will immediately compress any data buffered within cctx.
* `dstCapacity` must be large enough to ensure the operation will be successful.
* `cOptPtr` is optional : it's possible to provide NULL, all options will be set to default.
* @return : number of bytes written into dstBuffer (it can be zero, which means there was no data stored within cctx)
* or an error code if it fails (which can be tested using LZ4F_isError())
*/
LZ4FLIB_API size_t LZ4F_compressEnd(LZ4F_cctx* cctx, void* dstBuffer, size_t dstCapacity, const LZ4F_compressOptions_t* cOptPtr);
/* LZ4F_compressEnd() :
* To properly finish an LZ4 frame, invoke LZ4F_compressEnd().
* It will flush whatever data remained within `cctx` (like LZ4_flush())
* and properly finalize the frame, with an endMark and a checksum.
* `cOptPtr` is optional : NULL can be provided, in which case all options will be set to default.
* @return : number of bytes written into dstBuffer (necessarily >= 4 (endMark), or 8 if optional frame checksum is enabled)
* or an error code if it fails (which can be tested using LZ4F_isError())
* A successful call to LZ4F_compressEnd() makes `cctx` available again for another compression task.
*/
/*-*********************************
* Decompression functions
***********************************/
typedef struct LZ4F_dctx_s LZ4F_dctx; /* incomplete type */
typedef LZ4F_dctx* LZ4F_decompressionContext_t; /* compatibility with previous API versions */
typedef struct {
unsigned stableDst; /* guarantee that decompressed data will still be there on next function calls (avoid storage into tmp buffers) */
unsigned reserved[3];
} LZ4F_decompressOptions_t;
/* Resource management */
/*!LZ4F_createDecompressionContext() :
* Create an LZ4F_decompressionContext_t object, which will be used to track all decompression operations.
* The version provided MUST be LZ4F_VERSION. It is intended to track potential breaking differences between different versions.
* The function will provide a pointer to a fully allocated and initialized LZ4F_decompressionContext_t object.
* The result is an errorCode, which can be tested using LZ4F_isError().
* dctx memory can be released using LZ4F_freeDecompressionContext();
* The result of LZ4F_freeDecompressionContext() is indicative of the current state of decompressionContext when being released.
* That is, it should be == 0 if decompression has been completed fully and correctly.
*/
LZ4FLIB_API LZ4F_errorCode_t LZ4F_createDecompressionContext(LZ4F_dctx** dctxPtr, unsigned version);
LZ4FLIB_API LZ4F_errorCode_t LZ4F_freeDecompressionContext(LZ4F_dctx* const dctx);
/*====== Decompression ======*/
/*!LZ4F_getFrameInfo() :
* This function decodes frame header information (such as max blockSize, frame checksum, etc.).
* Its usage is optional. The objective is to extract frame header information, typically for allocation purposes.
* A header size is variable and can length from 7 to 15 bytes. It's possible to provide more input bytes than that.
* The number of bytes consumed from srcBuffer will be updated within *srcSizePtr (necessarily <= original value).
* Decompression must resume from this point (srcBuffer + *srcSizePtr).
* Note that LZ4F_getFrameInfo() can also be used anytime *after* decompression is started, in which case 0 input byte can be enough.
* Frame header info is *copied into* an already allocated LZ4F_frameInfo_t structure.
* @return : an hint about how many srcSize bytes LZ4F_decompress() expects for next call,
* or an error code which can be tested using LZ4F_isError()
* (typically, when there is not enough src bytes to fully decode the frame header)
*/
LZ4FLIB_API size_t LZ4F_getFrameInfo(LZ4F_dctx* dctx,
LZ4F_frameInfo_t* frameInfoPtr,
const void* srcBuffer, size_t* srcSizePtr);
/*!LZ4F_decompress() :
* Call this function repetitively to regenerate data compressed within `srcBuffer`.
* The function will attempt to decode up to *srcSizePtr bytes from srcBuffer, into dstBuffer of capacity *dstSizePtr.
*
* The number of bytes regenerated into dstBuffer will be provided within *dstSizePtr (necessarily <= original value).
*
* The number of bytes read from srcBuffer will be provided within *srcSizePtr (necessarily <= original value).
* Number of bytes read can be < number of bytes provided, meaning there is some more data to decode.
* It typically happens when dstBuffer is not large enough to contain all decoded data.
* Remaining data will have to be presented again in a subsequent invocation.
*
* `dstBuffer` content is expected to be flushed between each invocation, as its content will be overwritten.
* `dstBuffer` can be changed at will between each consecutive function invocation.
*
* @return is an hint of how many `srcSize` bytes LZ4F_decompress() expects for next call.
* Schematically, it's the size of the current (or remaining) compressed block + header of next block.
* Respecting the hint provides some boost to performance, since it does skip intermediate buffers.
* This is just a hint though, it's always possible to provide any srcSize.
* When a frame is fully decoded, @return will be 0 (no more data expected).
* If decompression failed, @return is an error code, which can be tested using LZ4F_isError().
*
* After a frame is fully decoded, dctx can be used again to decompress another frame.
*/
LZ4FLIB_API size_t LZ4F_decompress(LZ4F_dctx* dctx,
void* dstBuffer, size_t* dstSizePtr,
const void* srcBuffer, size_t* srcSizePtr,
const LZ4F_decompressOptions_t* dOptPtr);
#if defined (__cplusplus)
}
#endif
#endif /* LZ4F_H_09782039843 */

View File

@ -0,0 +1,82 @@
/*
LZ4 auto-framing library
Header File for static linking only
Copyright (C) 2011-2016, Yann Collet.
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
You can contact the author at :
- LZ4 source repository : https://github.com/lz4/lz4
- LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
*/
#ifndef LZ4FRAME_STATIC_H_0398209384
#define LZ4FRAME_STATIC_H_0398209384
#if defined (__cplusplus)
extern "C" {
#endif
/* lz4frame_static.h should be used solely in the context of static linking.
* It contains definitions which are not stable and may change in the future.
* Never use it in the context of DLL linking.
* */
/* --- Dependency --- */
#include "lz4frame.h"
/* --- Error List --- */
#define LZ4F_LIST_ERRORS(ITEM) \
ITEM(OK_NoError) ITEM(ERROR_GENERIC) \
ITEM(ERROR_maxBlockSize_invalid) ITEM(ERROR_blockMode_invalid) ITEM(ERROR_contentChecksumFlag_invalid) \
ITEM(ERROR_compressionLevel_invalid) \
ITEM(ERROR_headerVersion_wrong) ITEM(ERROR_blockChecksum_unsupported) ITEM(ERROR_reservedFlag_set) \
ITEM(ERROR_allocation_failed) \
ITEM(ERROR_srcSize_tooLarge) ITEM(ERROR_dstMaxSize_tooSmall) \
ITEM(ERROR_frameHeader_incomplete) ITEM(ERROR_frameType_unknown) ITEM(ERROR_frameSize_wrong) \
ITEM(ERROR_srcPtr_wrong) \
ITEM(ERROR_decompressionFailed) \
ITEM(ERROR_headerChecksum_invalid) ITEM(ERROR_contentChecksum_invalid) \
ITEM(ERROR_maxCode)
#define LZ4F_DISABLE_OLD_ENUMS /* comment to enable deprecated enums */
#ifndef LZ4F_DISABLE_OLD_ENUMS
# define LZ4F_GENERATE_ENUM(ENUM) LZ4F_##ENUM, ENUM = LZ4F_##ENUM,
#else
# define LZ4F_GENERATE_ENUM(ENUM) LZ4F_##ENUM,
#endif
typedef enum { LZ4F_LIST_ERRORS(LZ4F_GENERATE_ENUM) } LZ4F_errorCodes; /* enum is exposed, to handle specific errors; compare function result to -enum value */
LZ4F_errorCodes LZ4F_getErrorCode(size_t functionResult);
#if defined (__cplusplus)
}
#endif
#endif /* LZ4FRAME_STATIC_H_0398209384 */

View File

@ -0,0 +1,720 @@
/*
LZ4 HC - High Compression Mode of LZ4
Copyright (C) 2011-2016, Yann Collet.
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
You can contact the author at :
- LZ4 source repository : https://github.com/lz4/lz4
- LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
*/
/* note : lz4hc is not an independent module, it requires lz4.h/lz4.c for proper compilation */
/* *************************************
* Tuning Parameter
***************************************/
/*!
* HEAPMODE :
* Select how default compression function will allocate workplace memory,
* in stack (0:fastest), or in heap (1:requires malloc()).
* Since workplace is rather large, heap mode is recommended.
*/
#ifndef LZ4HC_HEAPMODE
# define LZ4HC_HEAPMODE 1
#endif
/* *************************************
* Dependency
***************************************/
#include "lz4hc.h"
/* *************************************
* Local Compiler Options
***************************************/
#if defined(__GNUC__)
# pragma GCC diagnostic ignored "-Wunused-function"
#endif
#if defined (__clang__)
# pragma clang diagnostic ignored "-Wunused-function"
#endif
/* *************************************
* Common LZ4 definition
***************************************/
#define LZ4_COMMONDEFS_ONLY
#include "lz4.c"
/* *************************************
* Local Constants
***************************************/
#define OPTIMAL_ML (int)((ML_MASK-1)+MINMATCH)
/**************************************
* Local Macros
**************************************/
#define HASH_FUNCTION(i) (((i) * 2654435761U) >> ((MINMATCH*8)-LZ4HC_HASH_LOG))
#define DELTANEXTMAXD(p) chainTable[(p) & LZ4HC_MAXD_MASK] /* flexible, LZ4HC_MAXD dependent */
#define DELTANEXTU16(p) chainTable[(U16)(p)] /* faster */
static U32 LZ4HC_hashPtr(const void* ptr) { return HASH_FUNCTION(LZ4_read32(ptr)); }
/**************************************
* HC Compression
**************************************/
static void LZ4HC_init (LZ4HC_CCtx_internal* hc4, const BYTE* start)
{
MEM_INIT((void*)hc4->hashTable, 0, sizeof(hc4->hashTable));
MEM_INIT(hc4->chainTable, 0xFF, sizeof(hc4->chainTable));
hc4->nextToUpdate = 64 KB;
hc4->base = start - 64 KB;
hc4->end = start;
hc4->dictBase = start - 64 KB;
hc4->dictLimit = 64 KB;
hc4->lowLimit = 64 KB;
}
/* Update chains up to ip (excluded) */
FORCE_INLINE void LZ4HC_Insert (LZ4HC_CCtx_internal* hc4, const BYTE* ip)
{
U16* const chainTable = hc4->chainTable;
U32* const hashTable = hc4->hashTable;
const BYTE* const base = hc4->base;
U32 const target = (U32)(ip - base);
U32 idx = hc4->nextToUpdate;
while (idx < target) {
U32 const h = LZ4HC_hashPtr(base+idx);
size_t delta = idx - hashTable[h];
if (delta>MAX_DISTANCE) delta = MAX_DISTANCE;
DELTANEXTU16(idx) = (U16)delta;
hashTable[h] = idx;
idx++;
}
hc4->nextToUpdate = target;
}
FORCE_INLINE int LZ4HC_InsertAndFindBestMatch (LZ4HC_CCtx_internal* hc4, /* Index table will be updated */
const BYTE* ip, const BYTE* const iLimit,
const BYTE** matchpos,
const int maxNbAttempts)
{
U16* const chainTable = hc4->chainTable;
U32* const HashTable = hc4->hashTable;
const BYTE* const base = hc4->base;
const BYTE* const dictBase = hc4->dictBase;
const U32 dictLimit = hc4->dictLimit;
const U32 lowLimit = (hc4->lowLimit + 64 KB > (U32)(ip-base)) ? hc4->lowLimit : (U32)(ip - base) - (64 KB - 1);
U32 matchIndex;
int nbAttempts=maxNbAttempts;
size_t ml=0;
/* HC4 match finder */
LZ4HC_Insert(hc4, ip);
matchIndex = HashTable[LZ4HC_hashPtr(ip)];
while ((matchIndex>=lowLimit) && (nbAttempts)) {
nbAttempts--;
if (matchIndex >= dictLimit) {
const BYTE* const match = base + matchIndex;
if (*(match+ml) == *(ip+ml)
&& (LZ4_read32(match) == LZ4_read32(ip)))
{
size_t const mlt = LZ4_count(ip+MINMATCH, match+MINMATCH, iLimit) + MINMATCH;
if (mlt > ml) { ml = mlt; *matchpos = match; }
}
} else {
const BYTE* const match = dictBase + matchIndex;
if (LZ4_read32(match) == LZ4_read32(ip)) {
size_t mlt;
const BYTE* vLimit = ip + (dictLimit - matchIndex);
if (vLimit > iLimit) vLimit = iLimit;
mlt = LZ4_count(ip+MINMATCH, match+MINMATCH, vLimit) + MINMATCH;
if ((ip+mlt == vLimit) && (vLimit < iLimit))
mlt += LZ4_count(ip+mlt, base+dictLimit, iLimit);
if (mlt > ml) { ml = mlt; *matchpos = base + matchIndex; } /* virtual matchpos */
}
}
matchIndex -= DELTANEXTU16(matchIndex);
}
return (int)ml;
}
FORCE_INLINE int LZ4HC_InsertAndGetWiderMatch (
LZ4HC_CCtx_internal* hc4,
const BYTE* const ip,
const BYTE* const iLowLimit,
const BYTE* const iHighLimit,
int longest,
const BYTE** matchpos,
const BYTE** startpos,
const int maxNbAttempts)
{
U16* const chainTable = hc4->chainTable;
U32* const HashTable = hc4->hashTable;
const BYTE* const base = hc4->base;
const U32 dictLimit = hc4->dictLimit;
const BYTE* const lowPrefixPtr = base + dictLimit;
const U32 lowLimit = (hc4->lowLimit + 64 KB > (U32)(ip-base)) ? hc4->lowLimit : (U32)(ip - base) - (64 KB - 1);
const BYTE* const dictBase = hc4->dictBase;
U32 matchIndex;
int nbAttempts = maxNbAttempts;
int delta = (int)(ip-iLowLimit);
/* First Match */
LZ4HC_Insert(hc4, ip);
matchIndex = HashTable[LZ4HC_hashPtr(ip)];
while ((matchIndex>=lowLimit) && (nbAttempts)) {
nbAttempts--;
if (matchIndex >= dictLimit) {
const BYTE* matchPtr = base + matchIndex;
if (*(iLowLimit + longest) == *(matchPtr - delta + longest)) {
if (LZ4_read32(matchPtr) == LZ4_read32(ip)) {
int mlt = MINMATCH + LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, iHighLimit);
int back = 0;
while ((ip+back > iLowLimit)
&& (matchPtr+back > lowPrefixPtr)
&& (ip[back-1] == matchPtr[back-1]))
back--;
mlt -= back;
if (mlt > longest) {
longest = (int)mlt;
*matchpos = matchPtr+back;
*startpos = ip+back;
}
}
}
} else {
const BYTE* const matchPtr = dictBase + matchIndex;
if (LZ4_read32(matchPtr) == LZ4_read32(ip)) {
size_t mlt;
int back=0;
const BYTE* vLimit = ip + (dictLimit - matchIndex);
if (vLimit > iHighLimit) vLimit = iHighLimit;
mlt = LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, vLimit) + MINMATCH;
if ((ip+mlt == vLimit) && (vLimit < iHighLimit))
mlt += LZ4_count(ip+mlt, base+dictLimit, iHighLimit);
while ((ip+back > iLowLimit) && (matchIndex+back > lowLimit) && (ip[back-1] == matchPtr[back-1])) back--;
mlt -= back;
if ((int)mlt > longest) { longest = (int)mlt; *matchpos = base + matchIndex + back; *startpos = ip+back; }
}
}
matchIndex -= DELTANEXTU16(matchIndex);
}
return longest;
}
typedef enum { noLimit = 0, limitedOutput = 1 } limitedOutput_directive;
#define LZ4HC_DEBUG 0
#if LZ4HC_DEBUG
static unsigned debug = 0;
#endif
FORCE_INLINE int LZ4HC_encodeSequence (
const BYTE** ip,
BYTE** op,
const BYTE** anchor,
int matchLength,
const BYTE* const match,
limitedOutput_directive limitedOutputBuffer,
BYTE* oend)
{
int length;
BYTE* token;
#if LZ4HC_DEBUG
if (debug) printf("literal : %u -- match : %u -- offset : %u\n", (U32)(*ip - *anchor), (U32)matchLength, (U32)(*ip-match));
#endif
/* Encode Literal length */
length = (int)(*ip - *anchor);
token = (*op)++;
if ((limitedOutputBuffer) && ((*op + (length>>8) + length + (2 + 1 + LASTLITERALS)) > oend)) return 1; /* Check output limit */
if (length>=(int)RUN_MASK) { int len; *token=(RUN_MASK<<ML_BITS); len = length-RUN_MASK; for(; len > 254 ; len-=255) *(*op)++ = 255; *(*op)++ = (BYTE)len; }
else *token = (BYTE)(length<<ML_BITS);
/* Copy Literals */
LZ4_wildCopy(*op, *anchor, (*op) + length);
*op += length;
/* Encode Offset */
LZ4_writeLE16(*op, (U16)(*ip-match)); *op += 2;
/* Encode MatchLength */
length = (int)(matchLength-MINMATCH);
if ((limitedOutputBuffer) && (*op + (length>>8) + (1 + LASTLITERALS) > oend)) return 1; /* Check output limit */
if (length>=(int)ML_MASK) {
*token += ML_MASK;
length -= ML_MASK;
for(; length > 509 ; length-=510) { *(*op)++ = 255; *(*op)++ = 255; }
if (length > 254) { length-=255; *(*op)++ = 255; }
*(*op)++ = (BYTE)length;
} else {
*token += (BYTE)(length);
}
/* Prepare next loop */
*ip += matchLength;
*anchor = *ip;
return 0;
}
#include "lz4opt.h"
static int LZ4HC_compress_hashChain (
LZ4HC_CCtx_internal* const ctx,
const char* const source,
char* const dest,
int const inputSize,
int const maxOutputSize,
unsigned maxNbAttempts,
limitedOutput_directive limit
)
{
const BYTE* ip = (const BYTE*) source;
const BYTE* anchor = ip;
const BYTE* const iend = ip + inputSize;
const BYTE* const mflimit = iend - MFLIMIT;
const BYTE* const matchlimit = (iend - LASTLITERALS);
BYTE* op = (BYTE*) dest;
BYTE* const oend = op + maxOutputSize;
int ml, ml2, ml3, ml0;
const BYTE* ref = NULL;
const BYTE* start2 = NULL;
const BYTE* ref2 = NULL;
const BYTE* start3 = NULL;
const BYTE* ref3 = NULL;
const BYTE* start0;
const BYTE* ref0;
/* init */
ctx->end += inputSize;
ip++;
/* Main Loop */
while (ip < mflimit) {
ml = LZ4HC_InsertAndFindBestMatch (ctx, ip, matchlimit, (&ref), maxNbAttempts);
if (!ml) { ip++; continue; }
/* saved, in case we would skip too much */
start0 = ip;
ref0 = ref;
ml0 = ml;
_Search2:
if (ip+ml < mflimit)
ml2 = LZ4HC_InsertAndGetWiderMatch(ctx, ip + ml - 2, ip + 0, matchlimit, ml, &ref2, &start2, maxNbAttempts);
else ml2 = ml;
if (ml2 == ml) { /* No better match */
if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ref, limit, oend)) return 0;
continue;
}
if (start0 < ip) {
if (start2 < ip + ml0) { /* empirical */
ip = start0;
ref = ref0;
ml = ml0;
}
}
/* Here, start0==ip */
if ((start2 - ip) < 3) { /* First Match too small : removed */
ml = ml2;
ip = start2;
ref =ref2;
goto _Search2;
}
_Search3:
/*
* Currently we have :
* ml2 > ml1, and
* ip1+3 <= ip2 (usually < ip1+ml1)
*/
if ((start2 - ip) < OPTIMAL_ML) {
int correction;
int new_ml = ml;
if (new_ml > OPTIMAL_ML) new_ml = OPTIMAL_ML;
if (ip+new_ml > start2 + ml2 - MINMATCH) new_ml = (int)(start2 - ip) + ml2 - MINMATCH;
correction = new_ml - (int)(start2 - ip);
if (correction > 0) {
start2 += correction;
ref2 += correction;
ml2 -= correction;
}
}
/* Now, we have start2 = ip+new_ml, with new_ml = min(ml, OPTIMAL_ML=18) */
if (start2 + ml2 < mflimit)
ml3 = LZ4HC_InsertAndGetWiderMatch(ctx, start2 + ml2 - 3, start2, matchlimit, ml2, &ref3, &start3, maxNbAttempts);
else ml3 = ml2;
if (ml3 == ml2) { /* No better match : 2 sequences to encode */
/* ip & ref are known; Now for ml */
if (start2 < ip+ml) ml = (int)(start2 - ip);
/* Now, encode 2 sequences */
if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ref, limit, oend)) return 0;
ip = start2;
if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml2, ref2, limit, oend)) return 0;
continue;
}
if (start3 < ip+ml+3) { /* Not enough space for match 2 : remove it */
if (start3 >= (ip+ml)) { /* can write Seq1 immediately ==> Seq2 is removed, so Seq3 becomes Seq1 */
if (start2 < ip+ml) {
int correction = (int)(ip+ml - start2);
start2 += correction;
ref2 += correction;
ml2 -= correction;
if (ml2 < MINMATCH) {
start2 = start3;
ref2 = ref3;
ml2 = ml3;
}
}
if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ref, limit, oend)) return 0;
ip = start3;
ref = ref3;
ml = ml3;
start0 = start2;
ref0 = ref2;
ml0 = ml2;
goto _Search2;
}
start2 = start3;
ref2 = ref3;
ml2 = ml3;
goto _Search3;
}
/*
* OK, now we have 3 ascending matches; let's write at least the first one
* ip & ref are known; Now for ml
*/
if (start2 < ip+ml) {
if ((start2 - ip) < (int)ML_MASK) {
int correction;
if (ml > OPTIMAL_ML) ml = OPTIMAL_ML;
if (ip + ml > start2 + ml2 - MINMATCH) ml = (int)(start2 - ip) + ml2 - MINMATCH;
correction = ml - (int)(start2 - ip);
if (correction > 0) {
start2 += correction;
ref2 += correction;
ml2 -= correction;
}
} else {
ml = (int)(start2 - ip);
}
}
if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ref, limit, oend)) return 0;
ip = start2;
ref = ref2;
ml = ml2;
start2 = start3;
ref2 = ref3;
ml2 = ml3;
goto _Search3;
}
/* Encode Last Literals */
{ int lastRun = (int)(iend - anchor);
if ((limit) && (((char*)op - dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize)) return 0; /* Check output limit */
if (lastRun>=(int)RUN_MASK) { *op++=(RUN_MASK<<ML_BITS); lastRun-=RUN_MASK; for(; lastRun > 254 ; lastRun-=255) *op++ = 255; *op++ = (BYTE) lastRun; }
else *op++ = (BYTE)(lastRun<<ML_BITS);
memcpy(op, anchor, iend - anchor);
op += iend-anchor;
}
/* End */
return (int) (((char*)op)-dest);
}
static int LZ4HC_getSearchNum(int compressionLevel)
{
switch (compressionLevel) {
default: return 0; /* unused */
case 11: return 128;
case 12: return 1<<10;
}
}
static int LZ4HC_compress_generic (
LZ4HC_CCtx_internal* const ctx,
const char* const source,
char* const dest,
int const inputSize,
int const maxOutputSize,
int compressionLevel,
limitedOutput_directive limit
)
{
if (compressionLevel < 1) compressionLevel = LZ4HC_CLEVEL_DEFAULT;
if (compressionLevel > 9) {
switch (compressionLevel) {
case 10: return LZ4HC_compress_hashChain(ctx, source, dest, inputSize, maxOutputSize, 1 << (16-1), limit);
case 11: ctx->searchNum = LZ4HC_getSearchNum(compressionLevel); return LZ4HC_compress_optimal(ctx, source, dest, inputSize, maxOutputSize, limit, 128, 0);
default:
case 12: ctx->searchNum = LZ4HC_getSearchNum(compressionLevel); return LZ4HC_compress_optimal(ctx, source, dest, inputSize, maxOutputSize, limit, LZ4_OPT_NUM, 1);
}
}
return LZ4HC_compress_hashChain(ctx, source, dest, inputSize, maxOutputSize, 1 << (compressionLevel-1), limit);
}
int LZ4_sizeofStateHC(void) { return sizeof(LZ4_streamHC_t); }
int LZ4_compress_HC_extStateHC (void* state, const char* src, char* dst, int srcSize, int maxDstSize, int compressionLevel)
{
LZ4HC_CCtx_internal* ctx = &((LZ4_streamHC_t*)state)->internal_donotuse;
if (((size_t)(state)&(sizeof(void*)-1)) != 0) return 0; /* Error : state is not aligned for pointers (32 or 64 bits) */
LZ4HC_init (ctx, (const BYTE*)src);
if (maxDstSize < LZ4_compressBound(srcSize))
return LZ4HC_compress_generic (ctx, src, dst, srcSize, maxDstSize, compressionLevel, limitedOutput);
else
return LZ4HC_compress_generic (ctx, src, dst, srcSize, maxDstSize, compressionLevel, noLimit);
}
int LZ4_compress_HC(const char* src, char* dst, int srcSize, int maxDstSize, int compressionLevel)
{
#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1
LZ4_streamHC_t* const statePtr = (LZ4_streamHC_t*)malloc(sizeof(LZ4_streamHC_t));
#else
LZ4_streamHC_t state;
LZ4_streamHC_t* const statePtr = &state;
#endif
int const cSize = LZ4_compress_HC_extStateHC(statePtr, src, dst, srcSize, maxDstSize, compressionLevel);
#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1
free(statePtr);
#endif
return cSize;
}
/**************************************
* Streaming Functions
**************************************/
/* allocation */
LZ4_streamHC_t* LZ4_createStreamHC(void) { return (LZ4_streamHC_t*)malloc(sizeof(LZ4_streamHC_t)); }
int LZ4_freeStreamHC (LZ4_streamHC_t* LZ4_streamHCPtr) { free(LZ4_streamHCPtr); return 0; }
/* initialization */
void LZ4_resetStreamHC (LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel)
{
LZ4_STATIC_ASSERT(sizeof(LZ4HC_CCtx_internal) <= sizeof(size_t) * LZ4_STREAMHCSIZE_SIZET); /* if compilation fails here, LZ4_STREAMHCSIZE must be increased */
LZ4_streamHCPtr->internal_donotuse.base = NULL;
LZ4_streamHCPtr->internal_donotuse.compressionLevel = (unsigned)compressionLevel;
LZ4_streamHCPtr->internal_donotuse.searchNum = LZ4HC_getSearchNum(compressionLevel);
}
int LZ4_loadDictHC (LZ4_streamHC_t* LZ4_streamHCPtr, const char* dictionary, int dictSize)
{
LZ4HC_CCtx_internal* ctxPtr = &LZ4_streamHCPtr->internal_donotuse;
if (dictSize > 64 KB) {
dictionary += dictSize - 64 KB;
dictSize = 64 KB;
}
LZ4HC_init (ctxPtr, (const BYTE*)dictionary);
ctxPtr->end = (const BYTE*)dictionary + dictSize;
if (ctxPtr->compressionLevel >= LZ4HC_CLEVEL_OPT_MIN)
LZ4HC_updateBinTree(ctxPtr, ctxPtr->end - MFLIMIT, ctxPtr->end - LASTLITERALS);
else
if (dictSize >= 4) LZ4HC_Insert (ctxPtr, ctxPtr->end-3);
return dictSize;
}
/* compression */
static void LZ4HC_setExternalDict(LZ4HC_CCtx_internal* ctxPtr, const BYTE* newBlock)
{
if (ctxPtr->compressionLevel >= LZ4HC_CLEVEL_OPT_MIN)
LZ4HC_updateBinTree(ctxPtr, ctxPtr->end - MFLIMIT, ctxPtr->end - LASTLITERALS);
else
if (ctxPtr->end >= ctxPtr->base + 4) LZ4HC_Insert (ctxPtr, ctxPtr->end-3); /* Referencing remaining dictionary content */
/* Only one memory segment for extDict, so any previous extDict is lost at this stage */
ctxPtr->lowLimit = ctxPtr->dictLimit;
ctxPtr->dictLimit = (U32)(ctxPtr->end - ctxPtr->base);
ctxPtr->dictBase = ctxPtr->base;
ctxPtr->base = newBlock - ctxPtr->dictLimit;
ctxPtr->end = newBlock;
ctxPtr->nextToUpdate = ctxPtr->dictLimit; /* match referencing will resume from there */
}
static int LZ4_compressHC_continue_generic (LZ4_streamHC_t* LZ4_streamHCPtr,
const char* source, char* dest,
int inputSize, int maxOutputSize, limitedOutput_directive limit)
{
LZ4HC_CCtx_internal* ctxPtr = &LZ4_streamHCPtr->internal_donotuse;
/* auto-init if forgotten */
if (ctxPtr->base == NULL) LZ4HC_init (ctxPtr, (const BYTE*) source);
/* Check overflow */
if ((size_t)(ctxPtr->end - ctxPtr->base) > 2 GB) {
size_t dictSize = (size_t)(ctxPtr->end - ctxPtr->base) - ctxPtr->dictLimit;
if (dictSize > 64 KB) dictSize = 64 KB;
LZ4_loadDictHC(LZ4_streamHCPtr, (const char*)(ctxPtr->end) - dictSize, (int)dictSize);
}
/* Check if blocks follow each other */
if ((const BYTE*)source != ctxPtr->end) LZ4HC_setExternalDict(ctxPtr, (const BYTE*)source);
/* Check overlapping input/dictionary space */
{ const BYTE* sourceEnd = (const BYTE*) source + inputSize;
const BYTE* const dictBegin = ctxPtr->dictBase + ctxPtr->lowLimit;
const BYTE* const dictEnd = ctxPtr->dictBase + ctxPtr->dictLimit;
if ((sourceEnd > dictBegin) && ((const BYTE*)source < dictEnd)) {
if (sourceEnd > dictEnd) sourceEnd = dictEnd;
ctxPtr->lowLimit = (U32)(sourceEnd - ctxPtr->dictBase);
if (ctxPtr->dictLimit - ctxPtr->lowLimit < 4) ctxPtr->lowLimit = ctxPtr->dictLimit;
}
}
return LZ4HC_compress_generic (ctxPtr, source, dest, inputSize, maxOutputSize, ctxPtr->compressionLevel, limit);
}
int LZ4_compress_HC_continue (LZ4_streamHC_t* LZ4_streamHCPtr, const char* source, char* dest, int inputSize, int maxOutputSize)
{
if (maxOutputSize < LZ4_compressBound(inputSize))
return LZ4_compressHC_continue_generic (LZ4_streamHCPtr, source, dest, inputSize, maxOutputSize, limitedOutput);
else
return LZ4_compressHC_continue_generic (LZ4_streamHCPtr, source, dest, inputSize, maxOutputSize, noLimit);
}
/* dictionary saving */
int LZ4_saveDictHC (LZ4_streamHC_t* LZ4_streamHCPtr, char* safeBuffer, int dictSize)
{
LZ4HC_CCtx_internal* const streamPtr = &LZ4_streamHCPtr->internal_donotuse;
int const prefixSize = (int)(streamPtr->end - (streamPtr->base + streamPtr->dictLimit));
if (dictSize > 64 KB) dictSize = 64 KB;
if (dictSize < 4) dictSize = 0;
if (dictSize > prefixSize) dictSize = prefixSize;
memmove(safeBuffer, streamPtr->end - dictSize, dictSize);
{ U32 const endIndex = (U32)(streamPtr->end - streamPtr->base);
streamPtr->end = (const BYTE*)safeBuffer + dictSize;
streamPtr->base = streamPtr->end - endIndex;
streamPtr->dictLimit = endIndex - dictSize;
streamPtr->lowLimit = endIndex - dictSize;
if (streamPtr->nextToUpdate < streamPtr->dictLimit) streamPtr->nextToUpdate = streamPtr->dictLimit;
}
return dictSize;
}
/***********************************
* Deprecated Functions
***********************************/
/* These functions currently generate deprecation warnings */
/* Deprecated compression functions */
int LZ4_compressHC(const char* src, char* dst, int srcSize) { return LZ4_compress_HC (src, dst, srcSize, LZ4_compressBound(srcSize), 0); }
int LZ4_compressHC_limitedOutput(const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC(src, dst, srcSize, maxDstSize, 0); }
int LZ4_compressHC2(const char* src, char* dst, int srcSize, int cLevel) { return LZ4_compress_HC (src, dst, srcSize, LZ4_compressBound(srcSize), cLevel); }
int LZ4_compressHC2_limitedOutput(const char* src, char* dst, int srcSize, int maxDstSize, int cLevel) { return LZ4_compress_HC(src, dst, srcSize, maxDstSize, cLevel); }
int LZ4_compressHC_withStateHC (void* state, const char* src, char* dst, int srcSize) { return LZ4_compress_HC_extStateHC (state, src, dst, srcSize, LZ4_compressBound(srcSize), 0); }
int LZ4_compressHC_limitedOutput_withStateHC (void* state, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC_extStateHC (state, src, dst, srcSize, maxDstSize, 0); }
int LZ4_compressHC2_withStateHC (void* state, const char* src, char* dst, int srcSize, int cLevel) { return LZ4_compress_HC_extStateHC(state, src, dst, srcSize, LZ4_compressBound(srcSize), cLevel); }
int LZ4_compressHC2_limitedOutput_withStateHC (void* state, const char* src, char* dst, int srcSize, int maxDstSize, int cLevel) { return LZ4_compress_HC_extStateHC(state, src, dst, srcSize, maxDstSize, cLevel); }
int LZ4_compressHC_continue (LZ4_streamHC_t* ctx, const char* src, char* dst, int srcSize) { return LZ4_compress_HC_continue (ctx, src, dst, srcSize, LZ4_compressBound(srcSize)); }
int LZ4_compressHC_limitedOutput_continue (LZ4_streamHC_t* ctx, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC_continue (ctx, src, dst, srcSize, maxDstSize); }
/* Deprecated streaming functions */
int LZ4_sizeofStreamStateHC(void) { return LZ4_STREAMHCSIZE; }
int LZ4_resetStreamStateHC(void* state, char* inputBuffer)
{
LZ4HC_CCtx_internal *ctx = &((LZ4_streamHC_t*)state)->internal_donotuse;
if ((((size_t)state) & (sizeof(void*)-1)) != 0) return 1; /* Error : pointer is not aligned for pointer (32 or 64 bits) */
LZ4HC_init(ctx, (const BYTE*)inputBuffer);
ctx->inputBuffer = (BYTE*)inputBuffer;
return 0;
}
void* LZ4_createHC (char* inputBuffer)
{
LZ4_streamHC_t* hc4 = (LZ4_streamHC_t*)ALLOCATOR(1, sizeof(LZ4_streamHC_t));
if (hc4 == NULL) return NULL; /* not enough memory */
LZ4HC_init (&hc4->internal_donotuse, (const BYTE*)inputBuffer);
hc4->internal_donotuse.inputBuffer = (BYTE*)inputBuffer;
return hc4;
}
int LZ4_freeHC (void* LZ4HC_Data) { FREEMEM(LZ4HC_Data); return 0; }
int LZ4_compressHC2_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int compressionLevel)
{
return LZ4HC_compress_generic (&((LZ4_streamHC_t*)LZ4HC_Data)->internal_donotuse, source, dest, inputSize, 0, compressionLevel, noLimit);
}
int LZ4_compressHC2_limitedOutput_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel)
{
return LZ4HC_compress_generic (&((LZ4_streamHC_t*)LZ4HC_Data)->internal_donotuse, source, dest, inputSize, maxOutputSize, compressionLevel, limitedOutput);
}
char* LZ4_slideInputBufferHC(void* LZ4HC_Data)
{
LZ4HC_CCtx_internal* const hc4 = &((LZ4_streamHC_t*)LZ4HC_Data)->internal_donotuse;
int const dictSize = LZ4_saveDictHC((LZ4_streamHC_t*)LZ4HC_Data, (char*)(hc4->inputBuffer), 64 KB);
return (char*)(hc4->inputBuffer + dictSize);
}

View File

@ -0,0 +1,228 @@
/*
LZ4 HC - High Compression Mode of LZ4
Header File
Copyright (C) 2011-2016, Yann Collet.
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
You can contact the author at :
- LZ4 source repository : https://github.com/lz4/lz4
- LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
*/
#ifndef LZ4_HC_H_19834876238432
#define LZ4_HC_H_19834876238432
#if defined (__cplusplus)
extern "C" {
#endif
/* --- Dependency --- */
/* note : lz4hc is not an independent module, it requires lz4.h/lz4.c for proper compilation */
#include "lz4.h" /* stddef, LZ4LIB_API, LZ4_DEPRECATED */
/* --- Useful constants --- */
#define LZ4HC_CLEVEL_MIN 3
#define LZ4HC_CLEVEL_DEFAULT 9
#define LZ4HC_CLEVEL_OPT_MIN 11
#define LZ4HC_CLEVEL_MAX 12
/*-************************************
* Block Compression
**************************************/
/*! LZ4_compress_HC() :
* Compress data from `src` into `dst`, using the more powerful but slower "HC" algorithm.
* `dst` must be already allocated.
* Compression is guaranteed to succeed if `dstCapacity >= LZ4_compressBound(srcSize)` (see "lz4.h")
* Max supported `srcSize` value is LZ4_MAX_INPUT_SIZE (see "lz4.h")
* `compressionLevel` : Recommended values are between 4 and 9, although any value between 1 and LZ4HC_MAX_CLEVEL will work.
* Values >LZ4HC_MAX_CLEVEL behave the same as LZ4HC_MAX_CLEVEL.
* @return : the number of bytes written into 'dst'
* or 0 if compression fails.
*/
LZ4LIB_API int LZ4_compress_HC (const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel);
/* Note :
* Decompression functions are provided within "lz4.h" (BSD license)
*/
/*! LZ4_compress_HC_extStateHC() :
* Same as LZ4_compress_HC(), but using an externally allocated memory segment for `state`.
* `state` size is provided by LZ4_sizeofStateHC().
* Memory segment must be aligned on 8-bytes boundaries (which a normal malloc() will do properly).
*/
LZ4LIB_API int LZ4_compress_HC_extStateHC(void* state, const char* src, char* dst, int srcSize, int maxDstSize, int compressionLevel);
LZ4LIB_API int LZ4_sizeofStateHC(void);
/*-************************************
* Streaming Compression
* Bufferless synchronous API
**************************************/
typedef union LZ4_streamHC_u LZ4_streamHC_t; /* incomplete type (defined later) */
/*! LZ4_createStreamHC() and LZ4_freeStreamHC() :
* These functions create and release memory for LZ4 HC streaming state.
* Newly created states are automatically initialized.
* Existing states can be re-used several times, using LZ4_resetStreamHC().
* These methods are API and ABI stable, they can be used in combination with a DLL.
*/
LZ4LIB_API LZ4_streamHC_t* LZ4_createStreamHC(void);
LZ4LIB_API int LZ4_freeStreamHC (LZ4_streamHC_t* streamHCPtr);
LZ4LIB_API void LZ4_resetStreamHC (LZ4_streamHC_t* streamHCPtr, int compressionLevel);
LZ4LIB_API int LZ4_loadDictHC (LZ4_streamHC_t* streamHCPtr, const char* dictionary, int dictSize);
LZ4LIB_API int LZ4_compress_HC_continue (LZ4_streamHC_t* streamHCPtr, const char* src, char* dst, int srcSize, int maxDstSize);
LZ4LIB_API int LZ4_saveDictHC (LZ4_streamHC_t* streamHCPtr, char* safeBuffer, int maxDictSize);
/*
These functions compress data in successive blocks of any size, using previous blocks as dictionary.
One key assumption is that previous blocks (up to 64 KB) remain read-accessible while compressing next blocks.
There is an exception for ring buffers, which can be smaller than 64 KB.
Ring buffers scenario is automatically detected and handled by LZ4_compress_HC_continue().
Before starting compression, state must be properly initialized, using LZ4_resetStreamHC().
A first "fictional block" can then be designated as initial dictionary, using LZ4_loadDictHC() (Optional).
Then, use LZ4_compress_HC_continue() to compress each successive block.
Previous memory blocks (including initial dictionary when present) must remain accessible and unmodified during compression.
'dst' buffer should be sized to handle worst case scenarios, using LZ4_compressBound(), to ensure operation success.
If, for any reason, previous data blocks can't be preserved unmodified in memory during next compression block,
you must save it to a safer memory space, using LZ4_saveDictHC().
Return value of LZ4_saveDictHC() is the size of dictionary effectively saved into 'safeBuffer'.
*/
/*-******************************************
* !!!!! STATIC LINKING ONLY !!!!!
*******************************************/
/*-*************************************
* PRIVATE DEFINITIONS :
* Do not use these definitions.
* They are exposed to allow static allocation of `LZ4_streamHC_t`.
* Using these definitions makes the code vulnerable to potential API break when upgrading LZ4
**************************************/
#define LZ4HC_DICTIONARY_LOGSIZE 17
#define LZ4HC_MAXD (1<<LZ4HC_DICTIONARY_LOGSIZE)
#define LZ4HC_MAXD_MASK (LZ4HC_MAXD - 1)
#define LZ4HC_HASH_LOG 15
#define LZ4HC_HASHTABLESIZE (1 << LZ4HC_HASH_LOG)
#define LZ4HC_HASH_MASK (LZ4HC_HASHTABLESIZE - 1)
#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
#include <stdint.h>
typedef struct
{
uint32_t hashTable[LZ4HC_HASHTABLESIZE];
uint16_t chainTable[LZ4HC_MAXD];
const uint8_t* end; /* next block here to continue on current prefix */
const uint8_t* base; /* All index relative to this position */
const uint8_t* dictBase; /* alternate base for extDict */
uint8_t* inputBuffer; /* deprecated */
uint32_t dictLimit; /* below that point, need extDict */
uint32_t lowLimit; /* below that point, no more dict */
uint32_t nextToUpdate; /* index from which to continue dictionary update */
uint32_t searchNum; /* only for optimal parser */
uint32_t compressionLevel;
} LZ4HC_CCtx_internal;
#else
typedef struct
{
unsigned int hashTable[LZ4HC_HASHTABLESIZE];
unsigned short chainTable[LZ4HC_MAXD];
const unsigned char* end; /* next block here to continue on current prefix */
const unsigned char* base; /* All index relative to this position */
const unsigned char* dictBase; /* alternate base for extDict */
unsigned char* inputBuffer; /* deprecated */
unsigned int dictLimit; /* below that point, need extDict */
unsigned int lowLimit; /* below that point, no more dict */
unsigned int nextToUpdate; /* index from which to continue dictionary update */
unsigned int searchNum; /* only for optimal parser */
unsigned int compressionLevel;
} LZ4HC_CCtx_internal;
#endif
#define LZ4_STREAMHCSIZE (4*LZ4HC_HASHTABLESIZE + 2*LZ4HC_MAXD + 56) /* 393268 */
#define LZ4_STREAMHCSIZE_SIZET (LZ4_STREAMHCSIZE / sizeof(size_t))
union LZ4_streamHC_u {
size_t table[LZ4_STREAMHCSIZE_SIZET];
LZ4HC_CCtx_internal internal_donotuse;
}; /* previously typedef'd to LZ4_streamHC_t */
/*
LZ4_streamHC_t :
This structure allows static allocation of LZ4 HC streaming state.
State must be initialized using LZ4_resetStreamHC() before first use.
Static allocation shall only be used in combination with static linking.
When invoking LZ4 from a DLL, use create/free functions instead, which are API and ABI stable.
*/
/*-************************************
* Deprecated Functions
**************************************/
/* see lz4.h LZ4_DISABLE_DEPRECATE_WARNINGS to turn off deprecation warnings */
/* deprecated compression functions */
/* these functions will trigger warning messages in future releases */
LZ4_DEPRECATED("use LZ4_compress_HC() instead") int LZ4_compressHC (const char* source, char* dest, int inputSize);
LZ4_DEPRECATED("use LZ4_compress_HC() instead") int LZ4_compressHC_limitedOutput (const char* source, char* dest, int inputSize, int maxOutputSize);
LZ4_DEPRECATED("use LZ4_compress_HC() instead") int LZ4_compressHC2 (const char* source, char* dest, int inputSize, int compressionLevel);
LZ4_DEPRECATED("use LZ4_compress_HC() instead") int LZ4_compressHC2_limitedOutput (const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel);
LZ4_DEPRECATED("use LZ4_compress_HC_extStateHC() instead") int LZ4_compressHC_withStateHC (void* state, const char* source, char* dest, int inputSize);
LZ4_DEPRECATED("use LZ4_compress_HC_extStateHC() instead") int LZ4_compressHC_limitedOutput_withStateHC (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);
LZ4_DEPRECATED("use LZ4_compress_HC_extStateHC() instead") int LZ4_compressHC2_withStateHC (void* state, const char* source, char* dest, int inputSize, int compressionLevel);
LZ4_DEPRECATED("use LZ4_compress_HC_extStateHC() instead") int LZ4_compressHC2_limitedOutput_withStateHC(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel);
LZ4_DEPRECATED("use LZ4_compress_HC_continue() instead") int LZ4_compressHC_continue (LZ4_streamHC_t* LZ4_streamHCPtr, const char* source, char* dest, int inputSize);
LZ4_DEPRECATED("use LZ4_compress_HC_continue() instead") int LZ4_compressHC_limitedOutput_continue (LZ4_streamHC_t* LZ4_streamHCPtr, const char* source, char* dest, int inputSize, int maxOutputSize);
/* Deprecated Streaming functions using older model; should no longer be used */
LZ4_DEPRECATED("use LZ4_createStreamHC() instead") void* LZ4_createHC (char* inputBuffer);
LZ4_DEPRECATED("use LZ4_saveDictHC() instead") char* LZ4_slideInputBufferHC (void* LZ4HC_Data);
LZ4_DEPRECATED("use LZ4_freeStreamHC() instead") int LZ4_freeHC (void* LZ4HC_Data);
LZ4_DEPRECATED("use LZ4_compress_HC_continue() instead") int LZ4_compressHC2_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int compressionLevel);
LZ4_DEPRECATED("use LZ4_compress_HC_continue() instead") int LZ4_compressHC2_limitedOutput_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel);
LZ4_DEPRECATED("use LZ4_createStreamHC() instead") int LZ4_sizeofStreamStateHC(void);
LZ4_DEPRECATED("use LZ4_resetStreamHC() instead") int LZ4_resetStreamStateHC(void* state, char* inputBuffer);
#if defined (__cplusplus)
}
#endif
#endif /* LZ4_HC_H_19834876238432 */

View File

@ -0,0 +1,363 @@
/*
lz4opt.h - Optimal Mode of LZ4
Copyright (C) 2015-2016, Przemyslaw Skibinski <inikep@gmail.com>
Note : this file is intended to be included within lz4hc.c
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
You can contact the author at :
- LZ4 source repository : https://github.com/lz4/lz4
- LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
*/
#define LZ4_OPT_NUM (1<<12)
typedef struct
{
int off;
int len;
} LZ4HC_match_t;
typedef struct
{
int price;
int off;
int mlen;
int litlen;
} LZ4HC_optimal_t;
/* price in bits */
FORCE_INLINE size_t LZ4HC_literalsPrice(size_t litlen)
{
size_t price = 8*litlen;
if (litlen >= (size_t)RUN_MASK) price+=8*(1+(litlen-RUN_MASK)/255);
return price;
}
/* requires mlen >= MINMATCH */
FORCE_INLINE size_t LZ4HC_sequencePrice(size_t litlen, size_t mlen)
{
size_t price = 16 + 8; /* 16-bit offset + token */
price += LZ4HC_literalsPrice(litlen);
mlen -= MINMATCH;
if (mlen >= (size_t)ML_MASK) price+=8*(1+(mlen-ML_MASK)/255);
return price;
}
/*-*************************************
* Binary Tree search
***************************************/
FORCE_INLINE int LZ4HC_BinTree_InsertAndGetAllMatches (
LZ4HC_CCtx_internal* ctx,
const BYTE* const ip,
const BYTE* const iHighLimit,
size_t best_mlen,
LZ4HC_match_t* matches,
int* matchNum)
{
U16* const chainTable = ctx->chainTable;
U32* const HashTable = ctx->hashTable;
const BYTE* const base = ctx->base;
const U32 dictLimit = ctx->dictLimit;
const U32 current = (U32)(ip - base);
const U32 lowLimit = (ctx->lowLimit + MAX_DISTANCE > current) ? ctx->lowLimit : current - (MAX_DISTANCE - 1);
const BYTE* const dictBase = ctx->dictBase;
const BYTE* match;
int nbAttempts = ctx->searchNum;
int mnum = 0;
U16 *ptr0, *ptr1, delta0, delta1;
U32 matchIndex;
size_t matchLength = 0;
U32* HashPos;
if (ip + MINMATCH > iHighLimit) return 1;
/* HC4 match finder */
HashPos = &HashTable[LZ4HC_hashPtr(ip)];
matchIndex = *HashPos;
*HashPos = current;
ptr0 = &DELTANEXTMAXD(current*2+1);
ptr1 = &DELTANEXTMAXD(current*2);
delta0 = delta1 = (U16)(current - matchIndex);
while ((matchIndex < current) && (matchIndex>=lowLimit) && (nbAttempts)) {
nbAttempts--;
if (matchIndex >= dictLimit) {
match = base + matchIndex;
matchLength = LZ4_count(ip, match, iHighLimit);
} else {
const BYTE* vLimit = ip + (dictLimit - matchIndex);
match = dictBase + matchIndex;
if (vLimit > iHighLimit) vLimit = iHighLimit;
matchLength = LZ4_count(ip, match, vLimit);
if ((ip+matchLength == vLimit) && (vLimit < iHighLimit))
matchLength += LZ4_count(ip+matchLength, base+dictLimit, iHighLimit);
}
if (matchLength > best_mlen) {
best_mlen = matchLength;
if (matches) {
if (matchIndex >= dictLimit)
matches[mnum].off = (int)(ip - match);
else
matches[mnum].off = (int)(ip - (base + matchIndex)); /* virtual matchpos */
matches[mnum].len = (int)matchLength;
mnum++;
}
if (best_mlen > LZ4_OPT_NUM) break;
}
if (ip+matchLength >= iHighLimit) /* equal : no way to know if inf or sup */
break; /* drop , to guarantee consistency ; miss a bit of compression, but other solutions can corrupt the tree */
if (*(ip+matchLength) < *(match+matchLength)) {
*ptr0 = delta0;
ptr0 = &DELTANEXTMAXD(matchIndex*2);
if (*ptr0 == (U16)-1) break;
delta0 = *ptr0;
delta1 += delta0;
matchIndex -= delta0;
} else {
*ptr1 = delta1;
ptr1 = &DELTANEXTMAXD(matchIndex*2+1);
if (*ptr1 == (U16)-1) break;
delta1 = *ptr1;
delta0 += delta1;
matchIndex -= delta1;
}
}
*ptr0 = (U16)-1;
*ptr1 = (U16)-1;
if (matchNum) *matchNum = mnum;
/* if (best_mlen > 8) return best_mlen-8; */
if (!matchNum) return 1;
return 1;
}
FORCE_INLINE void LZ4HC_updateBinTree(LZ4HC_CCtx_internal* ctx, const BYTE* const ip, const BYTE* const iHighLimit)
{
const BYTE* const base = ctx->base;
const U32 target = (U32)(ip - base);
U32 idx = ctx->nextToUpdate;
while(idx < target)
idx += LZ4HC_BinTree_InsertAndGetAllMatches(ctx, base+idx, iHighLimit, 8, NULL, NULL);
}
/** Tree updater, providing best match */
FORCE_INLINE int LZ4HC_BinTree_GetAllMatches (
LZ4HC_CCtx_internal* ctx,
const BYTE* const ip, const BYTE* const iHighLimit,
size_t best_mlen, LZ4HC_match_t* matches, const int fullUpdate)
{
int mnum = 0;
if (ip < ctx->base + ctx->nextToUpdate) return 0; /* skipped area */
if (fullUpdate) LZ4HC_updateBinTree(ctx, ip, iHighLimit);
best_mlen = LZ4HC_BinTree_InsertAndGetAllMatches(ctx, ip, iHighLimit, best_mlen, matches, &mnum);
ctx->nextToUpdate = (U32)(ip - ctx->base + best_mlen);
return mnum;
}
#define SET_PRICE(pos, mlen, offset, litlen, price) \
{ \
while (last_pos < pos) { opt[last_pos+1].price = 1<<30; last_pos++; } \
opt[pos].mlen = (int)mlen; \
opt[pos].off = (int)offset; \
opt[pos].litlen = (int)litlen; \
opt[pos].price = (int)price; \
}
static int LZ4HC_compress_optimal (
LZ4HC_CCtx_internal* ctx,
const char* const source,
char* dest,
int inputSize,
int maxOutputSize,
limitedOutput_directive limit,
const size_t sufficient_len,
const int fullUpdate
)
{
LZ4HC_optimal_t opt[LZ4_OPT_NUM + 1];
LZ4HC_match_t matches[LZ4_OPT_NUM + 1];
const BYTE *inr = NULL;
size_t res, cur, cur2;
size_t i, llen, litlen, mlen, best_mlen, price, offset, best_off, match_num, last_pos;
const BYTE* ip = (const BYTE*) source;
const BYTE* anchor = ip;
const BYTE* const iend = ip + inputSize;
const BYTE* const mflimit = iend - MFLIMIT;
const BYTE* const matchlimit = (iend - LASTLITERALS);
BYTE* op = (BYTE*) dest;
BYTE* const oend = op + maxOutputSize;
/* init */
ctx->end += inputSize;
ip++;
/* Main Loop */
while (ip < mflimit) {
memset(opt, 0, sizeof(LZ4HC_optimal_t));
last_pos = 0;
llen = ip - anchor;
match_num = LZ4HC_BinTree_GetAllMatches(ctx, ip, matchlimit, MINMATCH-1, matches, fullUpdate);
if (!match_num) { ip++; continue; }
if ((size_t)matches[match_num-1].len > sufficient_len) {
best_mlen = matches[match_num-1].len;
best_off = matches[match_num-1].off;
cur = 0;
last_pos = 1;
goto encode;
}
/* set prices using matches at position = 0 */
for (i = 0; i < match_num; i++) {
mlen = (i>0) ? (size_t)matches[i-1].len+1 : MINMATCH;
best_mlen = (matches[i].len < LZ4_OPT_NUM) ? matches[i].len : LZ4_OPT_NUM;
while (mlen <= best_mlen) {
litlen = 0;
price = LZ4HC_sequencePrice(llen + litlen, mlen) - LZ4HC_literalsPrice(llen);
SET_PRICE(mlen, mlen, matches[i].off, litlen, price);
mlen++;
}
}
if (last_pos < MINMATCH) { ip++; continue; }
/* check further positions */
opt[0].mlen = opt[1].mlen = 1;
for (cur = 1; cur <= last_pos; cur++) {
inr = ip + cur;
if (opt[cur-1].mlen == 1) {
litlen = opt[cur-1].litlen + 1;
if (cur != litlen) {
price = opt[cur - litlen].price + LZ4HC_literalsPrice(litlen);
} else {
price = LZ4HC_literalsPrice(llen + litlen) - LZ4HC_literalsPrice(llen);
}
} else {
litlen = 1;
price = opt[cur - 1].price + LZ4HC_literalsPrice(litlen);
}
mlen = 1;
best_mlen = 0;
if (cur > last_pos || price < (size_t)opt[cur].price)
SET_PRICE(cur, mlen, best_mlen, litlen, price);
if (cur == last_pos || inr >= mflimit) break;
match_num = LZ4HC_BinTree_GetAllMatches(ctx, inr, matchlimit, MINMATCH-1, matches, fullUpdate);
if (match_num > 0 && (size_t)matches[match_num-1].len > sufficient_len) {
best_mlen = matches[match_num-1].len;
best_off = matches[match_num-1].off;
last_pos = cur + 1;
goto encode;
}
/* set prices using matches at position = cur */
for (i = 0; i < match_num; i++) {
mlen = (i>0) ? (size_t)matches[i-1].len+1 : MINMATCH;
cur2 = cur;
best_mlen = (cur2 + matches[i].len < LZ4_OPT_NUM) ? (size_t)matches[i].len : LZ4_OPT_NUM - cur2;
while (mlen <= best_mlen) {
if (opt[cur2].mlen == 1) {
litlen = opt[cur2].litlen;
if (cur2 != litlen)
price = opt[cur2 - litlen].price + LZ4HC_sequencePrice(litlen, mlen);
else
price = LZ4HC_sequencePrice(llen + litlen, mlen) - LZ4HC_literalsPrice(llen);
} else {
litlen = 0;
price = opt[cur2].price + LZ4HC_sequencePrice(litlen, mlen);
}
if (cur2 + mlen > last_pos || price < (size_t)opt[cur2 + mlen].price) { // || (((int)price == opt[cur2 + mlen].price) && (opt[cur2 + mlen-1].mlen == 1))) {
SET_PRICE(cur2 + mlen, mlen, matches[i].off, litlen, price);
}
mlen++;
}
}
} /* for (cur = 1; cur <= last_pos; cur++) */
best_mlen = opt[last_pos].mlen;
best_off = opt[last_pos].off;
cur = last_pos - best_mlen;
encode: /* cur, last_pos, best_mlen, best_off have to be set */
opt[0].mlen = 1;
while (1) {
mlen = opt[cur].mlen;
offset = opt[cur].off;
opt[cur].mlen = (int)best_mlen;
opt[cur].off = (int)best_off;
best_mlen = mlen;
best_off = offset;
if (mlen > cur) break;
cur -= mlen;
}
cur = 0;
while (cur < last_pos) {
mlen = opt[cur].mlen;
if (mlen == 1) { ip++; cur++; continue; }
offset = opt[cur].off;
cur += mlen;
res = LZ4HC_encodeSequence(&ip, &op, &anchor, (int)mlen, ip - offset, limit, oend);
if (res) return 0;
}
}
/* Encode Last Literals */
{ int lastRun = (int)(iend - anchor);
if ((limit) && (((char*)op - dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize)) return 0; /* Check output limit */
if (lastRun>=(int)RUN_MASK) { *op++=(RUN_MASK<<ML_BITS); lastRun-=RUN_MASK; for(; lastRun > 254 ; lastRun-=255) *op++ = 255; *op++ = (BYTE) lastRun; }
else *op++ = (BYTE)(lastRun<<ML_BITS);
memcpy(op, anchor, iend - anchor);
op += iend-anchor;
}
/* End */
return (int) ((char*)op-dest);
}

View File

@ -0,0 +1,889 @@
/*
* xxHash - Fast Hash algorithm
* Copyright (C) 2012-2016, Yann Collet
*
* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You can contact the author at :
* - xxHash homepage: http://www.xxhash.com
* - xxHash source repository : https://github.com/Cyan4973/xxHash
*/
/* *************************************
* Tuning parameters
***************************************/
/*!XXH_FORCE_MEMORY_ACCESS :
* By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable.
* Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal.
* The below switch allow to select different access method for improved performance.
* Method 0 (default) : use `memcpy()`. Safe and portable.
* Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable).
* This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`.
* Method 2 : direct access. This method doesn't depend on compiler but violate C standard.
* It can generate buggy code on targets which do not support unaligned memory accesses.
* But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6)
* See http://stackoverflow.com/a/32095106/646947 for details.
* Prefer these methods in priority order (0 > 1 > 2)
*/
#ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */
# if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) )
# define XXH_FORCE_MEMORY_ACCESS 2
# elif defined(__INTEL_COMPILER) || \
(defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) ))
# define XXH_FORCE_MEMORY_ACCESS 1
# endif
#endif
/*!XXH_ACCEPT_NULL_INPUT_POINTER :
* If the input pointer is a null pointer, xxHash default behavior is to trigger a memory access error, since it is a bad pointer.
* When this option is enabled, xxHash output for null input pointers will be the same as a null-length input.
* By default, this option is disabled. To enable it, uncomment below define :
*/
/* #define XXH_ACCEPT_NULL_INPUT_POINTER 1 */
/*!XXH_FORCE_NATIVE_FORMAT :
* By default, xxHash library provides endian-independent Hash values, based on little-endian convention.
* Results are therefore identical for little-endian and big-endian CPU.
* This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format.
* Should endian-independence be of no importance for your application, you may set the #define below to 1,
* to improve speed for Big-endian CPU.
* This option has no impact on Little_Endian CPU.
*/
#ifndef XXH_FORCE_NATIVE_FORMAT /* can be defined externally */
# define XXH_FORCE_NATIVE_FORMAT 0
#endif
/*!XXH_FORCE_ALIGN_CHECK :
* This is a minor performance trick, only useful with lots of very small keys.
* It means : check for aligned/unaligned input.
* The check costs one initial branch per hash; set to 0 when the input data
* is guaranteed to be aligned.
*/
#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */
# if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
# define XXH_FORCE_ALIGN_CHECK 0
# else
# define XXH_FORCE_ALIGN_CHECK 1
# endif
#endif
/* *************************************
* Includes & Memory related functions
***************************************/
/*! Modify the local functions below should you wish to use some other memory routines
* for malloc(), free() */
#include <stdlib.h>
static void* XXH_malloc(size_t s) { return malloc(s); }
static void XXH_free (void* p) { free(p); }
/*! and for memcpy() */
#include <string.h>
static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); }
#define XXH_STATIC_LINKING_ONLY
#include "xxhash.h"
/* *************************************
* Compiler Specific Options
***************************************/
#ifdef _MSC_VER /* Visual Studio */
# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
# define FORCE_INLINE static __forceinline
#else
# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */
# ifdef __GNUC__
# define FORCE_INLINE static inline __attribute__((always_inline))
# else
# define FORCE_INLINE static inline
# endif
# else
# define FORCE_INLINE static
# endif /* __STDC_VERSION__ */
#endif
/* *************************************
* Basic Types
***************************************/
#ifndef MEM_MODULE
# if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
# include <stdint.h>
typedef uint8_t BYTE;
typedef uint16_t U16;
typedef uint32_t U32;
typedef int32_t S32;
# else
typedef unsigned char BYTE;
typedef unsigned short U16;
typedef unsigned int U32;
typedef signed int S32;
# endif
#endif
#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))
/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */
static U32 XXH_read32(const void* memPtr) { return *(const U32*) memPtr; }
#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1))
/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */
/* currently only defined for gcc and icc */
typedef union { U32 u32; } __attribute__((packed)) unalign;
static U32 XXH_read32(const void* ptr) { return ((const unalign*)ptr)->u32; }
#else
/* portable and safe solution. Generally efficient.
* see : http://stackoverflow.com/a/32095106/646947
*/
static U32 XXH_read32(const void* memPtr)
{
U32 val;
memcpy(&val, memPtr, sizeof(val));
return val;
}
#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */
/* ****************************************
* Compiler-specific Functions and Macros
******************************************/
#define XXH_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
/* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */
#if defined(_MSC_VER)
# define XXH_rotl32(x,r) _rotl(x,r)
# define XXH_rotl64(x,r) _rotl64(x,r)
#else
# define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r)))
# define XXH_rotl64(x,r) ((x << r) | (x >> (64 - r)))
#endif
#if defined(_MSC_VER) /* Visual Studio */
# define XXH_swap32 _byteswap_ulong
#elif XXH_GCC_VERSION >= 403
# define XXH_swap32 __builtin_bswap32
#else
static U32 XXH_swap32 (U32 x)
{
return ((x << 24) & 0xff000000 ) |
((x << 8) & 0x00ff0000 ) |
((x >> 8) & 0x0000ff00 ) |
((x >> 24) & 0x000000ff );
}
#endif
/* *************************************
* Architecture Macros
***************************************/
typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess;
/* XXH_CPU_LITTLE_ENDIAN can be defined externally, for example on the compiler command line */
#ifndef XXH_CPU_LITTLE_ENDIAN
static const int g_one = 1;
# define XXH_CPU_LITTLE_ENDIAN (*(const char*)(&g_one))
#endif
/* ***************************
* Memory reads
*****************************/
typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment;
FORCE_INLINE U32 XXH_readLE32_align(const void* ptr, XXH_endianess endian, XXH_alignment align)
{
if (align==XXH_unaligned)
return endian==XXH_littleEndian ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr));
else
return endian==XXH_littleEndian ? *(const U32*)ptr : XXH_swap32(*(const U32*)ptr);
}
FORCE_INLINE U32 XXH_readLE32(const void* ptr, XXH_endianess endian)
{
return XXH_readLE32_align(ptr, endian, XXH_unaligned);
}
static U32 XXH_readBE32(const void* ptr)
{
return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr);
}
/* *************************************
* Macros
***************************************/
#define XXH_STATIC_ASSERT(c) { enum { XXH_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */
XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; }
/* *******************************************************************
* 32-bits hash functions
*********************************************************************/
static const U32 PRIME32_1 = 2654435761U;
static const U32 PRIME32_2 = 2246822519U;
static const U32 PRIME32_3 = 3266489917U;
static const U32 PRIME32_4 = 668265263U;
static const U32 PRIME32_5 = 374761393U;
static U32 XXH32_round(U32 seed, U32 input)
{
seed += input * PRIME32_2;
seed = XXH_rotl32(seed, 13);
seed *= PRIME32_1;
return seed;
}
FORCE_INLINE U32 XXH32_endian_align(const void* input, size_t len, U32 seed, XXH_endianess endian, XXH_alignment align)
{
const BYTE* p = (const BYTE*)input;
const BYTE* bEnd = p + len;
U32 h32;
#define XXH_get32bits(p) XXH_readLE32_align(p, endian, align)
#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
if (p==NULL) {
len=0;
bEnd=p=(const BYTE*)(size_t)16;
}
#endif
if (len>=16) {
const BYTE* const limit = bEnd - 16;
U32 v1 = seed + PRIME32_1 + PRIME32_2;
U32 v2 = seed + PRIME32_2;
U32 v3 = seed + 0;
U32 v4 = seed - PRIME32_1;
do {
v1 = XXH32_round(v1, XXH_get32bits(p)); p+=4;
v2 = XXH32_round(v2, XXH_get32bits(p)); p+=4;
v3 = XXH32_round(v3, XXH_get32bits(p)); p+=4;
v4 = XXH32_round(v4, XXH_get32bits(p)); p+=4;
} while (p<=limit);
h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18);
} else {
h32 = seed + PRIME32_5;
}
h32 += (U32) len;
while (p+4<=bEnd) {
h32 += XXH_get32bits(p) * PRIME32_3;
h32 = XXH_rotl32(h32, 17) * PRIME32_4 ;
p+=4;
}
while (p<bEnd) {
h32 += (*p) * PRIME32_5;
h32 = XXH_rotl32(h32, 11) * PRIME32_1 ;
p++;
}
h32 ^= h32 >> 15;
h32 *= PRIME32_2;
h32 ^= h32 >> 13;
h32 *= PRIME32_3;
h32 ^= h32 >> 16;
return h32;
}
XXH_PUBLIC_API unsigned int XXH32 (const void* input, size_t len, unsigned int seed)
{
#if 0
/* Simple version, good for code maintenance, but unfortunately slow for small inputs */
XXH32_state_t state;
XXH32_reset(&state, seed);
XXH32_update(&state, input, len);
return XXH32_digest(&state);
#else
XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
if (XXH_FORCE_ALIGN_CHECK) {
if ((((size_t)input) & 3) == 0) { /* Input is 4-bytes aligned, leverage the speed benefit */
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned);
else
return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned);
} }
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned);
else
return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned);
#endif
}
/*====== Hash streaming ======*/
XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void)
{
return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t));
}
XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr)
{
XXH_free(statePtr);
return XXH_OK;
}
XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState)
{
memcpy(dstState, srcState, sizeof(*dstState));
}
XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, unsigned int seed)
{
XXH32_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */
memset(&state, 0, sizeof(state)-4); /* do not write into reserved, for future removal */
state.v1 = seed + PRIME32_1 + PRIME32_2;
state.v2 = seed + PRIME32_2;
state.v3 = seed + 0;
state.v4 = seed - PRIME32_1;
memcpy(statePtr, &state, sizeof(state));
return XXH_OK;
}
FORCE_INLINE XXH_errorcode XXH32_update_endian (XXH32_state_t* state, const void* input, size_t len, XXH_endianess endian)
{
const BYTE* p = (const BYTE*)input;
const BYTE* const bEnd = p + len;
#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
if (input==NULL) return XXH_ERROR;
#endif
state->total_len_32 += (unsigned)len;
state->large_len |= (len>=16) | (state->total_len_32>=16);
if (state->memsize + len < 16) { /* fill in tmp buffer */
XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, len);
state->memsize += (unsigned)len;
return XXH_OK;
}
if (state->memsize) { /* some data left from previous update */
XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, 16-state->memsize);
{ const U32* p32 = state->mem32;
state->v1 = XXH32_round(state->v1, XXH_readLE32(p32, endian)); p32++;
state->v2 = XXH32_round(state->v2, XXH_readLE32(p32, endian)); p32++;
state->v3 = XXH32_round(state->v3, XXH_readLE32(p32, endian)); p32++;
state->v4 = XXH32_round(state->v4, XXH_readLE32(p32, endian)); p32++;
}
p += 16-state->memsize;
state->memsize = 0;
}
if (p <= bEnd-16) {
const BYTE* const limit = bEnd - 16;
U32 v1 = state->v1;
U32 v2 = state->v2;
U32 v3 = state->v3;
U32 v4 = state->v4;
do {
v1 = XXH32_round(v1, XXH_readLE32(p, endian)); p+=4;
v2 = XXH32_round(v2, XXH_readLE32(p, endian)); p+=4;
v3 = XXH32_round(v3, XXH_readLE32(p, endian)); p+=4;
v4 = XXH32_round(v4, XXH_readLE32(p, endian)); p+=4;
} while (p<=limit);
state->v1 = v1;
state->v2 = v2;
state->v3 = v3;
state->v4 = v4;
}
if (p < bEnd) {
XXH_memcpy(state->mem32, p, (size_t)(bEnd-p));
state->memsize = (unsigned)(bEnd-p);
}
return XXH_OK;
}
XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* state_in, const void* input, size_t len)
{
XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
return XXH32_update_endian(state_in, input, len, XXH_littleEndian);
else
return XXH32_update_endian(state_in, input, len, XXH_bigEndian);
}
FORCE_INLINE U32 XXH32_digest_endian (const XXH32_state_t* state, XXH_endianess endian)
{
const BYTE * p = (const BYTE*)state->mem32;
const BYTE* const bEnd = (const BYTE*)(state->mem32) + state->memsize;
U32 h32;
if (state->large_len) {
h32 = XXH_rotl32(state->v1, 1) + XXH_rotl32(state->v2, 7) + XXH_rotl32(state->v3, 12) + XXH_rotl32(state->v4, 18);
} else {
h32 = state->v3 /* == seed */ + PRIME32_5;
}
h32 += state->total_len_32;
while (p+4<=bEnd) {
h32 += XXH_readLE32(p, endian) * PRIME32_3;
h32 = XXH_rotl32(h32, 17) * PRIME32_4;
p+=4;
}
while (p<bEnd) {
h32 += (*p) * PRIME32_5;
h32 = XXH_rotl32(h32, 11) * PRIME32_1;
p++;
}
h32 ^= h32 >> 15;
h32 *= PRIME32_2;
h32 ^= h32 >> 13;
h32 *= PRIME32_3;
h32 ^= h32 >> 16;
return h32;
}
XXH_PUBLIC_API unsigned int XXH32_digest (const XXH32_state_t* state_in)
{
XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
return XXH32_digest_endian(state_in, XXH_littleEndian);
else
return XXH32_digest_endian(state_in, XXH_bigEndian);
}
/*====== Canonical representation ======*/
/*! Default XXH result types are basic unsigned 32 and 64 bits.
* The canonical representation follows human-readable write convention, aka big-endian (large digits first).
* These functions allow transformation of hash result into and from its canonical format.
* This way, hash values can be written into a file or buffer, and remain comparable across different systems and programs.
*/
XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash)
{
XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t));
if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash);
memcpy(dst, &hash, sizeof(*dst));
}
XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src)
{
return XXH_readBE32(src);
}
#ifndef XXH_NO_LONG_LONG
/* *******************************************************************
* 64-bits hash functions
*********************************************************************/
/*====== Memory access ======*/
#ifndef MEM_MODULE
# define MEM_MODULE
# if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
# include <stdint.h>
typedef uint64_t U64;
# else
typedef unsigned long long U64; /* if your compiler doesn't support unsigned long long, replace by another 64-bit type here. Note that xxhash.h will also need to be updated. */
# endif
#endif
#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))
/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */
static U64 XXH_read64(const void* memPtr) { return *(const U64*) memPtr; }
#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1))
/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */
/* currently only defined for gcc and icc */
typedef union { U32 u32; U64 u64; } __attribute__((packed)) unalign64;
static U64 XXH_read64(const void* ptr) { return ((const unalign64*)ptr)->u64; }
#else
/* portable and safe solution. Generally efficient.
* see : http://stackoverflow.com/a/32095106/646947
*/
static U64 XXH_read64(const void* memPtr)
{
U64 val;
memcpy(&val, memPtr, sizeof(val));
return val;
}
#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */
#if defined(_MSC_VER) /* Visual Studio */
# define XXH_swap64 _byteswap_uint64
#elif XXH_GCC_VERSION >= 403
# define XXH_swap64 __builtin_bswap64
#else
static U64 XXH_swap64 (U64 x)
{
return ((x << 56) & 0xff00000000000000ULL) |
((x << 40) & 0x00ff000000000000ULL) |
((x << 24) & 0x0000ff0000000000ULL) |
((x << 8) & 0x000000ff00000000ULL) |
((x >> 8) & 0x00000000ff000000ULL) |
((x >> 24) & 0x0000000000ff0000ULL) |
((x >> 40) & 0x000000000000ff00ULL) |
((x >> 56) & 0x00000000000000ffULL);
}
#endif
FORCE_INLINE U64 XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_alignment align)
{
if (align==XXH_unaligned)
return endian==XXH_littleEndian ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr));
else
return endian==XXH_littleEndian ? *(const U64*)ptr : XXH_swap64(*(const U64*)ptr);
}
FORCE_INLINE U64 XXH_readLE64(const void* ptr, XXH_endianess endian)
{
return XXH_readLE64_align(ptr, endian, XXH_unaligned);
}
static U64 XXH_readBE64(const void* ptr)
{
return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr);
}
/*====== xxh64 ======*/
static const U64 PRIME64_1 = 11400714785074694791ULL;
static const U64 PRIME64_2 = 14029467366897019727ULL;
static const U64 PRIME64_3 = 1609587929392839161ULL;
static const U64 PRIME64_4 = 9650029242287828579ULL;
static const U64 PRIME64_5 = 2870177450012600261ULL;
static U64 XXH64_round(U64 acc, U64 input)
{
acc += input * PRIME64_2;
acc = XXH_rotl64(acc, 31);
acc *= PRIME64_1;
return acc;
}
static U64 XXH64_mergeRound(U64 acc, U64 val)
{
val = XXH64_round(0, val);
acc ^= val;
acc = acc * PRIME64_1 + PRIME64_4;
return acc;
}
FORCE_INLINE U64 XXH64_endian_align(const void* input, size_t len, U64 seed, XXH_endianess endian, XXH_alignment align)
{
const BYTE* p = (const BYTE*)input;
const BYTE* const bEnd = p + len;
U64 h64;
#define XXH_get64bits(p) XXH_readLE64_align(p, endian, align)
#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
if (p==NULL) {
len=0;
bEnd=p=(const BYTE*)(size_t)32;
}
#endif
if (len>=32) {
const BYTE* const limit = bEnd - 32;
U64 v1 = seed + PRIME64_1 + PRIME64_2;
U64 v2 = seed + PRIME64_2;
U64 v3 = seed + 0;
U64 v4 = seed - PRIME64_1;
do {
v1 = XXH64_round(v1, XXH_get64bits(p)); p+=8;
v2 = XXH64_round(v2, XXH_get64bits(p)); p+=8;
v3 = XXH64_round(v3, XXH_get64bits(p)); p+=8;
v4 = XXH64_round(v4, XXH_get64bits(p)); p+=8;
} while (p<=limit);
h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18);
h64 = XXH64_mergeRound(h64, v1);
h64 = XXH64_mergeRound(h64, v2);
h64 = XXH64_mergeRound(h64, v3);
h64 = XXH64_mergeRound(h64, v4);
} else {
h64 = seed + PRIME64_5;
}
h64 += (U64) len;
while (p+8<=bEnd) {
U64 const k1 = XXH64_round(0, XXH_get64bits(p));
h64 ^= k1;
h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4;
p+=8;
}
if (p+4<=bEnd) {
h64 ^= (U64)(XXH_get32bits(p)) * PRIME64_1;
h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3;
p+=4;
}
while (p<bEnd) {
h64 ^= (*p) * PRIME64_5;
h64 = XXH_rotl64(h64, 11) * PRIME64_1;
p++;
}
h64 ^= h64 >> 33;
h64 *= PRIME64_2;
h64 ^= h64 >> 29;
h64 *= PRIME64_3;
h64 ^= h64 >> 32;
return h64;
}
XXH_PUBLIC_API unsigned long long XXH64 (const void* input, size_t len, unsigned long long seed)
{
#if 0
/* Simple version, good for code maintenance, but unfortunately slow for small inputs */
XXH64_state_t state;
XXH64_reset(&state, seed);
XXH64_update(&state, input, len);
return XXH64_digest(&state);
#else
XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
if (XXH_FORCE_ALIGN_CHECK) {
if ((((size_t)input) & 7)==0) { /* Input is aligned, let's leverage the speed advantage */
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned);
else
return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned);
} }
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned);
else
return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned);
#endif
}
/*====== Hash Streaming ======*/
XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void)
{
return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t));
}
XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr)
{
XXH_free(statePtr);
return XXH_OK;
}
XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dstState, const XXH64_state_t* srcState)
{
memcpy(dstState, srcState, sizeof(*dstState));
}
XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, unsigned long long seed)
{
XXH64_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */
memset(&state, 0, sizeof(state)-8); /* do not write into reserved, for future removal */
state.v1 = seed + PRIME64_1 + PRIME64_2;
state.v2 = seed + PRIME64_2;
state.v3 = seed + 0;
state.v4 = seed - PRIME64_1;
memcpy(statePtr, &state, sizeof(state));
return XXH_OK;
}
FORCE_INLINE XXH_errorcode XXH64_update_endian (XXH64_state_t* state, const void* input, size_t len, XXH_endianess endian)
{
const BYTE* p = (const BYTE*)input;
const BYTE* const bEnd = p + len;
#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
if (input==NULL) return XXH_ERROR;
#endif
state->total_len += len;
if (state->memsize + len < 32) { /* fill in tmp buffer */
XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, len);
state->memsize += (U32)len;
return XXH_OK;
}
if (state->memsize) { /* tmp buffer is full */
XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, 32-state->memsize);
state->v1 = XXH64_round(state->v1, XXH_readLE64(state->mem64+0, endian));
state->v2 = XXH64_round(state->v2, XXH_readLE64(state->mem64+1, endian));
state->v3 = XXH64_round(state->v3, XXH_readLE64(state->mem64+2, endian));
state->v4 = XXH64_round(state->v4, XXH_readLE64(state->mem64+3, endian));
p += 32-state->memsize;
state->memsize = 0;
}
if (p+32 <= bEnd) {
const BYTE* const limit = bEnd - 32;
U64 v1 = state->v1;
U64 v2 = state->v2;
U64 v3 = state->v3;
U64 v4 = state->v4;
do {
v1 = XXH64_round(v1, XXH_readLE64(p, endian)); p+=8;
v2 = XXH64_round(v2, XXH_readLE64(p, endian)); p+=8;
v3 = XXH64_round(v3, XXH_readLE64(p, endian)); p+=8;
v4 = XXH64_round(v4, XXH_readLE64(p, endian)); p+=8;
} while (p<=limit);
state->v1 = v1;
state->v2 = v2;
state->v3 = v3;
state->v4 = v4;
}
if (p < bEnd) {
XXH_memcpy(state->mem64, p, (size_t)(bEnd-p));
state->memsize = (unsigned)(bEnd-p);
}
return XXH_OK;
}
XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* state_in, const void* input, size_t len)
{
XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
return XXH64_update_endian(state_in, input, len, XXH_littleEndian);
else
return XXH64_update_endian(state_in, input, len, XXH_bigEndian);
}
FORCE_INLINE U64 XXH64_digest_endian (const XXH64_state_t* state, XXH_endianess endian)
{
const BYTE * p = (const BYTE*)state->mem64;
const BYTE* const bEnd = (const BYTE*)state->mem64 + state->memsize;
U64 h64;
if (state->total_len >= 32) {
U64 const v1 = state->v1;
U64 const v2 = state->v2;
U64 const v3 = state->v3;
U64 const v4 = state->v4;
h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18);
h64 = XXH64_mergeRound(h64, v1);
h64 = XXH64_mergeRound(h64, v2);
h64 = XXH64_mergeRound(h64, v3);
h64 = XXH64_mergeRound(h64, v4);
} else {
h64 = state->v3 + PRIME64_5;
}
h64 += (U64) state->total_len;
while (p+8<=bEnd) {
U64 const k1 = XXH64_round(0, XXH_readLE64(p, endian));
h64 ^= k1;
h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4;
p+=8;
}
if (p+4<=bEnd) {
h64 ^= (U64)(XXH_readLE32(p, endian)) * PRIME64_1;
h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3;
p+=4;
}
while (p<bEnd) {
h64 ^= (*p) * PRIME64_5;
h64 = XXH_rotl64(h64, 11) * PRIME64_1;
p++;
}
h64 ^= h64 >> 33;
h64 *= PRIME64_2;
h64 ^= h64 >> 29;
h64 *= PRIME64_3;
h64 ^= h64 >> 32;
return h64;
}
XXH_PUBLIC_API unsigned long long XXH64_digest (const XXH64_state_t* state_in)
{
XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
return XXH64_digest_endian(state_in, XXH_littleEndian);
else
return XXH64_digest_endian(state_in, XXH_bigEndian);
}
/*====== Canonical representation ======*/
XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash)
{
XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t));
if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash);
memcpy(dst, &hash, sizeof(*dst));
}
XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src)
{
return XXH_readBE64(src);
}
#endif /* XXH_NO_LONG_LONG */

View File

@ -0,0 +1,293 @@
/*
xxHash - Extremely Fast Hash algorithm
Header File
Copyright (C) 2012-2016, Yann Collet.
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
You can contact the author at :
- xxHash source repository : https://github.com/Cyan4973/xxHash
*/
/* Notice extracted from xxHash homepage :
xxHash is an extremely fast Hash algorithm, running at RAM speed limits.
It also successfully passes all tests from the SMHasher suite.
Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz)
Name Speed Q.Score Author
xxHash 5.4 GB/s 10
CrapWow 3.2 GB/s 2 Andrew
MumurHash 3a 2.7 GB/s 10 Austin Appleby
SpookyHash 2.0 GB/s 10 Bob Jenkins
SBox 1.4 GB/s 9 Bret Mulvey
Lookup3 1.2 GB/s 9 Bob Jenkins
SuperFastHash 1.2 GB/s 1 Paul Hsieh
CityHash64 1.05 GB/s 10 Pike & Alakuijala
FNV 0.55 GB/s 5 Fowler, Noll, Vo
CRC32 0.43 GB/s 9
MD5-32 0.33 GB/s 10 Ronald L. Rivest
SHA1-32 0.28 GB/s 10
Q.Score is a measure of quality of the hash function.
It depends on successfully passing SMHasher test set.
10 is a perfect score.
A 64-bits version, named XXH64, is available since r35.
It offers much better speed, but for 64-bits applications only.
Name Speed on 64 bits Speed on 32 bits
XXH64 13.8 GB/s 1.9 GB/s
XXH32 6.8 GB/s 6.0 GB/s
*/
#ifndef XXHASH_H_5627135585666179
#define XXHASH_H_5627135585666179 1
#if defined (__cplusplus)
extern "C" {
#endif
/* ****************************
* Definitions
******************************/
#include <stddef.h> /* size_t */
typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode;
/* ****************************
* API modifier
******************************/
/** XXH_PRIVATE_API
* This is useful to include xxhash functions in `static` mode
* in order to inline them, and remove their symbol from the public list.
* Methodology :
* #define XXH_PRIVATE_API
* #include "xxhash.h"
* `xxhash.c` is automatically included.
* It's not useful to compile and link it as a separate module.
*/
#ifdef XXH_PRIVATE_API
# ifndef XXH_STATIC_LINKING_ONLY
# define XXH_STATIC_LINKING_ONLY
# endif
# if defined(__GNUC__)
# define XXH_PUBLIC_API static __inline __attribute__((unused))
# elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
# define XXH_PUBLIC_API static inline
# elif defined(_MSC_VER)
# define XXH_PUBLIC_API static __inline
# else
# define XXH_PUBLIC_API static /* this version may generate warnings for unused static functions; disable the relevant warning */
# endif
#else
# define XXH_PUBLIC_API /* do nothing */
#endif /* XXH_PRIVATE_API */
/*!XXH_NAMESPACE, aka Namespace Emulation :
If you want to include _and expose_ xxHash functions from within your own library,
but also want to avoid symbol collisions with other libraries which may also include xxHash,
you can use XXH_NAMESPACE, to automatically prefix any public symbol from xxhash library
with the value of XXH_NAMESPACE (therefore, avoid NULL and numeric values).
Note that no change is required within the calling program as long as it includes `xxhash.h` :
regular symbol name will be automatically translated by this header.
*/
#ifdef XXH_NAMESPACE
# define XXH_CAT(A,B) A##B
# define XXH_NAME2(A,B) XXH_CAT(A,B)
# define XXH_versionNumber XXH_NAME2(XXH_NAMESPACE, XXH_versionNumber)
# define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32)
# define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState)
# define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState)
# define XXH32_reset XXH_NAME2(XXH_NAMESPACE, XXH32_reset)
# define XXH32_update XXH_NAME2(XXH_NAMESPACE, XXH32_update)
# define XXH32_digest XXH_NAME2(XXH_NAMESPACE, XXH32_digest)
# define XXH32_copyState XXH_NAME2(XXH_NAMESPACE, XXH32_copyState)
# define XXH32_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH32_canonicalFromHash)
# define XXH32_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH32_hashFromCanonical)
# define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64)
# define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState)
# define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState)
# define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset)
# define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update)
# define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest)
# define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState)
# define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash)
# define XXH64_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH64_hashFromCanonical)
#endif
/* *************************************
* Version
***************************************/
#define XXH_VERSION_MAJOR 0
#define XXH_VERSION_MINOR 6
#define XXH_VERSION_RELEASE 2
#define XXH_VERSION_NUMBER (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE)
XXH_PUBLIC_API unsigned XXH_versionNumber (void);
/*-**********************************************************************
* 32-bits hash
************************************************************************/
typedef unsigned int XXH32_hash_t;
/*! XXH32() :
Calculate the 32-bits hash of sequence "length" bytes stored at memory address "input".
The memory between input & input+length must be valid (allocated and read-accessible).
"seed" can be used to alter the result predictably.
Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s */
XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, unsigned int seed);
/*====== Streaming ======*/
typedef struct XXH32_state_s XXH32_state_t; /* incomplete type */
XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void);
XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr);
XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state);
XXH_PUBLIC_API XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, unsigned int seed);
XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length);
XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr);
/*
These functions generate the xxHash of an input provided in multiple segments.
Note that, for small input, they are slower than single-call functions, due to state management.
For small input, prefer `XXH32()` and `XXH64()` .
XXH state must first be allocated, using XXH*_createState() .
Start a new hash by initializing state with a seed, using XXH*_reset().
Then, feed the hash state by calling XXH*_update() as many times as necessary.
Obviously, input must be allocated and read accessible.
The function returns an error code, with 0 meaning OK, and any other value meaning there is an error.
Finally, a hash value can be produced anytime, by using XXH*_digest().
This function returns the nn-bits hash as an int or long long.
It's still possible to continue inserting input into the hash state after a digest,
and generate some new hashes later on, by calling again XXH*_digest().
When done, free XXH state space if it was allocated dynamically.
*/
/*====== Canonical representation ======*/
typedef struct { unsigned char digest[4]; } XXH32_canonical_t;
XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash);
XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src);
/* Default result type for XXH functions are primitive unsigned 32 and 64 bits.
* The canonical representation uses human-readable write convention, aka big-endian (large digits first).
* These functions allow transformation of hash result into and from its canonical format.
* This way, hash values can be written into a file / memory, and remain comparable on different systems and programs.
*/
#ifndef XXH_NO_LONG_LONG
/*-**********************************************************************
* 64-bits hash
************************************************************************/
typedef unsigned long long XXH64_hash_t;
/*! XXH64() :
Calculate the 64-bits hash of sequence of length "len" stored at memory address "input".
"seed" can be used to alter the result predictably.
This function runs faster on 64-bits systems, but slower on 32-bits systems (see benchmark).
*/
XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t length, unsigned long long seed);
/*====== Streaming ======*/
typedef struct XXH64_state_s XXH64_state_t; /* incomplete type */
XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void);
XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr);
XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dst_state, const XXH64_state_t* src_state);
XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH64_state_t* statePtr, unsigned long long seed);
XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void* input, size_t length);
XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* statePtr);
/*====== Canonical representation ======*/
typedef struct { unsigned char digest[8]; } XXH64_canonical_t;
XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash);
XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src);
#endif /* XXH_NO_LONG_LONG */
#ifdef XXH_STATIC_LINKING_ONLY
/* ================================================================================================
This section contains definitions which are not guaranteed to remain stable.
They may change in future versions, becoming incompatible with a different version of the library.
They shall only be used with static linking.
Never use these definitions in association with dynamic linking !
=================================================================================================== */
/* These definitions are only meant to allow allocation of XXH state
statically, on stack, or in a struct for example.
Do not use members directly. */
struct XXH32_state_s {
unsigned total_len_32;
unsigned large_len;
unsigned v1;
unsigned v2;
unsigned v3;
unsigned v4;
unsigned mem32[4]; /* buffer defined as U32 for alignment */
unsigned memsize;
unsigned reserved; /* never read nor write, will be removed in a future version */
}; /* typedef'd to XXH32_state_t */
#ifndef XXH_NO_LONG_LONG
struct XXH64_state_s {
unsigned long long total_len;
unsigned long long v1;
unsigned long long v2;
unsigned long long v3;
unsigned long long v4;
unsigned long long mem64[4]; /* buffer defined as U64 for alignment */
unsigned memsize;
unsigned reserved[2]; /* never read nor write, will be removed in a future version */
}; /* typedef'd to XXH64_state_t */
#endif
# ifdef XXH_PRIVATE_API
# include "xxhash.c" /* include xxhash function bodies as `static`, for inlining */
# endif
#endif /* XXH_STATIC_LINKING_ONLY */
#if defined (__cplusplus)
}
#endif
#endif /* XXHASH_H_5627135585666179 */

View File

@ -0,0 +1,2 @@
DisableFormat: true
SortIncludes: false

View File

@ -0,0 +1,395 @@
#
# libwebsockets - small server side websockets and web server implementation
#
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
#
# 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_directories(.)
macro(add_subdir_include_dirs arg1)
add_subdirectory(${arg1})
list(APPEND LWS_LIB_BUILD_INC_PATHS ${_CMAKE_INC_LIST})
endmacro()
set(LWS_LIB_INCLUDES "")
#
# Plat specific build items
#
if (LWS_PLAT_FREERTOS)
add_subdir_include_dirs(plat/freertos)
if (ESP_PLATFORM)
list(APPEND LWS_ESP_IDF_DIRS
$ENV{IDF_PATH}/components/freertos/include
$ENV{IDF_PATH}/components/esp_hw_support/include/soc/
$ENV{IDF_PATH}/components/esp_common/include
$ENV{IDF_PATH}/components/esp_timer/include
$ENV{IDF_PATH}/components/soc/include
$ENV{IDF_PATH}/components/soc/src/esp32/include
$ENV{IDF_PATH}/components/lwip/port/esp32/include
$ENV{IDF_PATH}/components/lwip/lwip/src/include
$ENV{IDF_PATH}/components/lwip/port/esp32/include
${CMAKE_BINARY_DIR}/config
$ENV{IDF_PATH}/components/esp_rom/include
$ENV{IDF_PATH}/components/esp_system/include
$ENV{IDF_PATH}/components/lwip/include/apps/sntp
$ENV{IDF_PATH}/components/soc/soc/esp32/include
$ENV{IDF_PATH}/components/heap/include
$ENV{IDF_PATH}/components/mbedtls/mbedtls/include
$ENV{IDF_PATH}/components/mbedtls/port/include
$ENV{IDF_PATH}/components/esp_wifi/include
$ENV{IDF_PATH}/components/esp_event/include
$ENV{IDF_PATH}/components/esp_netif/include
$ENV{IDF_PATH}/components/esp_eth/include
$ENV{IDF_PATH}/components/driver/include
$ENV{IDF_PATH}/components/soc/soc/include
$ENV{IDF_PATH}/components/tcpip_adapter/include
$ENV{IDF_PATH}/components/lwip/include/apps
$ENV{IDF_PATH}/components/nvs_flash/include
$ENV{IDF_PATH}/components/esp32/include
$ENV{IDF_PATH}/components/spi_flash/include
$ENV{IDF_PATH}/components/mdns/include
$ENV{IDF_PATH}/components/lwip/lwip/src/include/lwip
$ENV{IDF_PATH}/components/lwip/lwip/src/include
$ENV{IDF_PATH}/components/lwip/lwip/src/include/lwip
$ENV{IDF_PATH}/components/newlib/platform_include )
include_directories(${LWS_ESP_IDF_DIRS})
list(APPEND CMAKE_REQUIRED_INCLUDES ${LWS_ESP_IDF_DIRS})
endif()
else()
if (LWS_PLAT_BAREMETAL)
# add_subdir_include_dirs(plat/baremetal)
else()
if (LWS_PLAT_OPTEE)
add_subdir_include_dirs(plat/optee)
else()
if (WIN32)
add_subdir_include_dirs(plat/windows)
else()
add_subdir_include_dirs(plat/unix)
endif()
endif()
endif()
endif()
if (LIB_LIST)
set(CMAKE_REQUIRED_LIBRARIES ${LIB_LIST} ${CMAKE_REQUIRED_LIBRARIES})
endif()
if (LWS_WITH_ZLIB)
if (LWS_WITH_BUNDLED_ZLIB)
if (WIN32)
# it's trying to delete internal zlib entry
LIST(REMOVE_AT CMAKE_REQUIRED_LIBRARIES 0 )
endif()
endif()
endif()
# ideally we want to use pipe2()
CHECK_C_SOURCE_COMPILES("
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <unistd.h>
int main(void) {
int fd[2];
return pipe2(fd, 0);
}" LWS_HAVE_PIPE2)
# tcp keepalive needs this on linux to work practically... but it only exists
# after kernel 2.6.37
CHECK_C_SOURCE_COMPILES("#include <netinet/tcp.h>\nint main(void) { return TCP_USER_TIMEOUT; }\n" LWS_HAVE_TCP_USER_TIMEOUT)
set(LWS_PUBLIC_INCLUDES "")
if (LWS_WITH_TLS)
add_subdir_include_dirs(tls)
endif()
# Generate the lws_config.h that includes all the private compilation settings.
configure_file(
"${PROJECT_SOURCE_DIR}/cmake/lws_config_private.h.in"
"${PROJECT_BINARY_DIR}/lws_config_private.h")
add_subdir_include_dirs(core)
add_subdir_include_dirs(misc)
add_subdir_include_dirs(system)
if (LWS_WITH_DRIVERS)
add_subdir_include_dirs(drivers)
endif()
if (LWS_WITH_NETWORK)
add_subdir_include_dirs(core-net)
add_subdir_include_dirs(roles)
endif()
if (LWS_WITH_JOSE)
add_subdir_include_dirs(jose)
endif()
if (LWS_WITH_COSE)
add_subdir_include_dirs(cose)
endif()
include_directories(secure-streams)
add_subdir_include_dirs(event-libs)
if (LWS_WITH_SECURE_STREAMS)
add_subdir_include_dirs(secure-streams)
endif()
add_subdir_include_dirs(secure-streams/serialized/client)
if (LWS_WITH_STATIC)
if (LWS_STATIC_PIC)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()
add_library(websockets STATIC ${SOURCES})# ${HDR_PUBLIC})
set_target_properties(websockets PROPERTIES LINKER_LANGUAGE C)
list(APPEND LWS_LIBRARIES websockets)
target_include_directories(websockets INTERFACE
$<INSTALL_INTERFACE:${LWS_INSTALL_INCLUDE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/../include>
)
target_include_directories(websockets PRIVATE ${LWS_LIB_BUILD_INC_PATHS})
target_compile_definitions(websockets PRIVATE LWS_BUILDING_STATIC)
target_include_directories(websockets PUBLIC ${LWS_PUBLIC_INCLUDES})
set(LWS_PUBLIC_INCLUDES ${LWS_PUBLIC_INCLUDES} PARENT_SCOPE)
if (WIN32)
# Windows uses the same .lib ending for static libraries and shared
# library linker files, so rename the static library.
set_target_properties(websockets
PROPERTIES
OUTPUT_NAME websockets_static)
endif()
endif()
if (LWS_WITH_SHARED)
if (NOT RESOURCES)
set(RESOURCES "")
endif()
add_library(websockets_shared SHARED ${SOURCES} ${RESOURCES})# ${HDR_PUBLIC})
set_target_properties(websockets_shared PROPERTIES LINKER_LANGUAGE C)
list(APPEND LWS_LIBRARIES websockets_shared)
target_include_directories(websockets_shared INTERFACE
$<INSTALL_INTERFACE:${LWS_INSTALL_INCLUDE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/../include>
)
target_include_directories(websockets_shared PRIVATE ${LWS_LIB_BUILD_INC_PATHS})
target_compile_definitions(websockets_shared PRIVATE LWS_BUILDING_SHARED)
target_include_directories(websockets_shared PUBLIC ${LWS_PUBLIC_INCLUDES})
set(LWS_PUBLIC_INCLUDES ${LWS_PUBLIC_INCLUDES} PARENT_SCOPE)
# We want the shared lib to be named "libwebsockets"
# not "libwebsocket_shared".
set_target_properties(websockets_shared
PROPERTIES
OUTPUT_NAME websockets)
if (WIN32)
# Compile as DLL (export function declarations)
set_property(
TARGET websockets_shared
PROPERTY COMPILE_DEFINITIONS
LWS_DLL
LWS_INTERNAL)
endif()
if (APPLE)
set_property(TARGET websockets_shared PROPERTY MACOSX_RPATH YES)
endif()
if (UNIX AND LWS_WITH_PLUGINS_API)
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
if (NOT((${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") OR
(${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD") OR
(${CMAKE_SYSTEM_NAME} MATCHES "QNX")))
if (LWS_WITH_SHARED)
target_link_libraries(websockets_shared dl)
endif()
endif()
endif()
endif()
#
# expose the library private include dirs to plugins, test apps etc that are
# part of the lib build but different targets
#
if (LWS_WITH_SHARED)
get_target_property(LWS_LIB_INCLUDES websockets_shared INCLUDE_DIRECTORIES)
else()
get_target_property(LWS_LIB_INCLUDES websockets INCLUDE_DIRECTORIES)
endif()
# Set the so version of the lib.
# Equivalent to LDFLAGS=-version-info x:x:x
if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR COMPILER_IS_CLANG)
foreach(lib ${LWS_LIBRARIES})
set_target_properties(${lib}
PROPERTIES
SOVERSION ${SOVERSION})
endforeach()
endif()
# Setup the linking for all libs.
foreach (lib ${LWS_LIBRARIES})
target_link_libraries(${lib} ${LIB_LIST})
endforeach()
#
# These will be available to parent projects including libwebsockets
# using add_subdirectory()
#
set(LIBWEBSOCKETS_LIBRARIES ${LWS_LIBRARIES} CACHE STRING "Libwebsocket libraries")
if (LWS_WITH_STATIC)
set(LIBWEBSOCKETS_LIBRARIES_STATIC websockets CACHE STRING "Libwebsocket static library")
endif()
if (LWS_WITH_SHARED)
set(LIBWEBSOCKETS_LIBRARIES_SHARED websockets_shared CACHE STRING "Libwebsocket shared library")
endif()
# Install libs and headers.
install(TARGETS ${LWS_LIBRARIES}
EXPORT LibwebsocketsTargets
LIBRARY DESTINATION "${LWS_INSTALL_LIB_DIR}${LIB_SUFFIX}" COMPONENT core
ARCHIVE DESTINATION "${LWS_INSTALL_LIB_DIR}${LIB_SUFFIX}" COMPONENT core
RUNTIME DESTINATION "${LWS_INSTALL_BIN_DIR}" COMPONENT core # Windows DLLs
PUBLIC_HEADER DESTINATION "${LWS_INSTALL_INCLUDE_DIR}" COMPONENT dev)
#set(CPACK_COMPONENT_LIBRARIES_DISPLAY_NAME "Libraries" PARENT_SCOPE)
set(CPACK_COMPONENT_DEV_DISPLAY_NAME "Development files" PARENT_SCOPE)
if (UNIX OR MINGW)
# figure out pkfcfg required libs here
set(lws_requires "")
if (LWS_HAVE_LIBCAP)
if (NOT lws_requires STREQUAL "")
set(lws_requires "${lws_requires},libcap")
else()
set(lws_requires "libcap")
endif()
endif()
# Generate and install pkgconfig.
# (This is not indented, because the tabs will be part of the output)
file(WRITE "${PROJECT_BINARY_DIR}/libwebsockets.pc"
"prefix=\"${CMAKE_INSTALL_PREFIX}\"
exec_prefix=\${prefix}
libdir=\${exec_prefix}/lib${LIB_SUFFIX}
includedir=\${prefix}/include
Name: libwebsockets
Description: Websockets server and client library
Version: ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}
Libs: -L\${libdir} -lwebsockets
Cflags: -I\${includedir}
"
)
if (NOT ${lws_requires} STREQUAL "")
file(APPEND "${PROJECT_BINARY_DIR}/libwebsockets.pc" "Requires: ${lws_requires}")
endif()
install(FILES "${PROJECT_BINARY_DIR}/libwebsockets.pc"
DESTINATION lib${LIB_SUFFIX}/pkgconfig)
file(WRITE "${PROJECT_BINARY_DIR}/libwebsockets_static.pc"
"prefix=\"${CMAKE_INSTALL_PREFIX}\"
exec_prefix=\${prefix}
libdir=\${exec_prefix}/lib${LIB_SUFFIX}
includedir=\${prefix}/include
Name: libwebsockets_static
Description: Websockets server and client static library
Version: ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}
Libs: -L\${libdir} -l:libwebsockets${CMAKE_STATIC_LIBRARY_SUFFIX}
Libs.private:
Cflags: -I\${includedir}
"
)
if (NOT ${lws_requires} STREQUAL "")
file(APPEND "${PROJECT_BINARY_DIR}/libwebsockets_static.pc" "Requires: ${lws_requires}")
endif()
install(FILES "${PROJECT_BINARY_DIR}/libwebsockets_static.pc"
DESTINATION lib${LIB_SUFFIX}/pkgconfig)
endif(UNIX OR MINGW)
# Keep explicit parent scope exports at end
#
export_to_parent_intermediate()
if (DEFINED LWS_PLAT_UNIX)
set(LWS_PLAT_UNIX ${LWS_PLAT_UNIX} PARENT_SCOPE)
if (ILLUMOS)
add_definitions("-D__illumos__")
endif()
endif()
set(LWS_HAVE_MBEDTLS_NET_SOCKETS ${LWS_HAVE_MBEDTLS_NET_SOCKETS} PARENT_SCOPE)
set(LWS_HAVE_MBEDTLS_SSL_NEW_SESSION_TICKET ${LWS_HAVE_MBEDTLS_SSL_NEW_SESSION_TICKET} PARENT_SCOPE)
set(LWS_HAVE_mbedtls_ssl_conf_alpn_protocols ${LWS_HAVE_mbedtls_ssl_conf_alpn_protocols} PARENT_SCOPE)
set(TEST_SERVER_SSL_KEY "${TEST_SERVER_SSL_KEY}" PARENT_SCOPE)
set(TEST_SERVER_SSL_CERT "${TEST_SERVER_SSL_CERT}" PARENT_SCOPE)
set(TEST_SERVER_DATA ${TEST_SERVER_DATA} PARENT_SCOPE)
set(LWS_HAVE_PIPE2 ${LWS_HAVE_PIPE2} PARENT_SCOPE)
set(LWS_LIBRARIES ${LWS_LIBRARIES} PARENT_SCOPE)
if (DEFINED WIN32_HELPERS_PATH)
set(WIN32_HELPERS_PATH ${WIN32_HELPERS_PATH} PARENT_SCOPE)
endif()
if (DEFINED HDR_PRIVATE)
set(HDR_PRIVATE ${HDR_PRIVATE} PARENT_SCOPE)
endif()
if (DEFINED ZLIB_FOUND)
set(ZLIB_FOUND ${ZLIB_FOUND} PARENT_SCOPE)
endif()
if (DEFINED LIB_LIST_AT_END)
set(LIB_LIST_AT_END ${LIB_LIST_AT_END} PARENT_SCOPE)
endif()
set(USE_WOLFSSL ${USE_WOLFSSL} PARENT_SCOPE)
set(LWS_DEPS_LIB_PATHS ${LWS_DEPS_LIB_PATHS} PARENT_SCOPE)

View File

@ -0,0 +1,16 @@
## Library sources layout
Code that goes in the libwebsockets library itself lives down ./lib
Path|Sources
---|---
lib/core|Core lws code related to generic fd and wsi servicing and management
lib/core-net|Core lws code that applies only if networking enabled
lib/event-libs|Code containing optional event-lib specific adaptations
lib/jose|JOSE / JWS / JWK / JWE implementations
lib/misc|Code for various mostly optional miscellaneous features
lib/plat|Platform-specific adaptation code
lib/roles|Code for specific optional wsi roles, eg, http/1, h2, ws, raw, etc
lib/system|Code for system-level features, eg, dhcpclient
lib/tls|Code supporting the various TLS libraries

View File

@ -0,0 +1,85 @@
#
# libwebsockets - small server side websockets and web server implementation
#
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
#
# 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_directories(.)
list(APPEND SOURCES
core-net/dummy-callback.c
core-net/output.c
core-net/close.c
core-net/network.c
core-net/vhost.c
core-net/pollfd.c
core-net/service.c
core-net/sorted-usec-list.c
core-net/wsi.c
core-net/wsi-timeout.c
core-net/adopt.c
roles/pipe/ops-pipe.c
)
if (LWS_WITH_SYS_STATE)
list(APPEND SOURCES
core-net/state.c
)
endif()
if (LWS_WITH_NETLINK)
list(APPEND SOURCES
core-net/route.c
)
endif()
if (LWS_WITH_LWS_DSH)
list(APPEND SOURCES
core-net/lws-dsh.c)
endif()
if (LWS_WITH_WOL)
list(APPEND SOURCES
core-net/wol.c)
endif()
if (LWS_WITH_CLIENT)
list(APPEND SOURCES
core-net/client/client.c
core-net/client/connect.c
core-net/client/connect2.c
core-net/client/connect3.c
core-net/client/connect4.c
core-net/client/sort-dns.c
)
if (LWS_WITH_CONMON)
list(APPEND SOURCES
core-net/client/conmon.c
)
endif()
endif()
if (LWS_WITH_SOCKS5 AND NOT LWS_WITHOUT_CLIENT)
list(APPEND SOURCES
core-net/socks5-client.c)
endif()
exports_to_parent_scope()

View File

@ -0,0 +1,58 @@
# Implementation background
## Client connection Queueing
By default lws treats each client connection as completely separate, and each is
made from scratch with its own network connection independently.
If the user code sets the `LCCSCF_PIPELINE` bit on `info.ssl_connection` when
creating the client connection though, lws attempts to optimize multiple client
connections to the same place by sharing any existing connection and its tls
tunnel where possible.
There are two basic approaches, for h1 additional connections of the same type
and endpoint basically queue on a leader and happen sequentially.
For muxed protocols like h2, they may also queue if the initial connection is
not up yet, but subsequently the will all join the existing connection
simultaneously "broadside".
## h1 queueing
The initial wsi to start the network connection becomes the "leader" that
subsequent connection attempts will queue against. Each vhost has a dll2_owner
`wsi->dll_cli_active_conns_owner` that "leaders" who are actually making network
connections themselves can register on as "active client connections".
Other client wsi being created who find there is already a leader on the active
client connection list for the vhost, can join their dll2 wsi->dll2_cli_txn_queue
to the leader's wsi->dll2_cli_txn_queue_owner to "queue" on the leader.
The user code does not know which wsi was first or is queued, it just waits for
stuff to happen the same either way.
When the "leader" wsi connects, it performs its client transaction as normal,
and at the end arrives at `lws_http_transaction_completed_client()`. Here, it
calls through to the lws_mux `_lws_generic_transaction_completed_active_conn()`
helper. This helper sees if anything else is queued, and if so, migrates assets
like the SSL *, the socket fd, and any remaining queue from the original leader
to the head of the list, which replaces the old leader as the "active client
connection" any subsequent connects would queue on.
It has to be done this way so that user code which may know each client wsi by
its wsi, or have marked it with an opaque_user_data pointer, is getting its
specific request handled by the wsi it expects it to be handled by.
A side effect of this, and in order to be able to handle POSTs cleanly, lws
does not attempt to send the headers for the next queued child before the
previous child has finished.
The process of moving the SSL context and fd etc between the queued wsi continues
until the queue is all handled.
## muxed protocol queueing and stream binding
h2 connections act the same as h1 before the initial connection has been made,
but once it is made all the queued connections join the network connection as
child mux streams immediately, "broadside", binding the stream to the existing
network connection.

View File

@ -0,0 +1,947 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* 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 "private-lib-core.h"
#include "private-lib-async-dns.h"
static int
lws_get_idlest_tsi(struct lws_context *context)
{
unsigned int lowest = ~0u;
int n = 0, hit = -1;
for (; n < context->count_threads; n++) {
lwsl_cx_debug(context, "%d %d\n", context->pt[n].fds_count,
context->fd_limit_per_thread - 1);
if ((unsigned int)context->pt[n].fds_count !=
context->fd_limit_per_thread - 1 &&
(unsigned int)context->pt[n].fds_count < lowest) {
lowest = context->pt[n].fds_count;
hit = n;
}
}
return hit;
}
struct lws *
lws_create_new_server_wsi(struct lws_vhost *vhost, int fixed_tsi, int group,
const char *desc)
{
struct lws *new_wsi;
int n = fixed_tsi;
if (n < 0)
n = lws_get_idlest_tsi(vhost->context);
if (n < 0) {
lwsl_vhost_err(vhost, "no space for new conn");
return NULL;
}
lws_context_lock(vhost->context, __func__);
new_wsi = __lws_wsi_create_with_role(vhost->context, n, NULL,
vhost->lc.log_cx);
lws_context_unlock(vhost->context);
if (new_wsi == NULL) {
lwsl_vhost_err(vhost, "OOM");
return NULL;
}
lws_wsi_fault_timedclose(new_wsi);
__lws_lc_tag(vhost->context, &vhost->context->lcg[group],
&new_wsi->lc, "%s|%s", vhost->name, desc);
new_wsi->wsistate |= LWSIFR_SERVER;
new_wsi->tsi = (char)n;
lwsl_wsi_debug(new_wsi, "joining vh %s, tsi %d",
vhost->name, new_wsi->tsi);
lws_vhost_bind_wsi(vhost, new_wsi);
new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
new_wsi->retry_policy = vhost->retry_policy;
/* initialize the instance struct */
lwsi_set_state(new_wsi, LRS_UNCONNECTED);
new_wsi->hdr_parsing_completed = 0;
#ifdef LWS_WITH_TLS
new_wsi->tls.use_ssl = LWS_SSL_ENABLED(vhost);
#endif
/*
* these can only be set once the protocol is known
* we set an un-established connection's protocol pointer
* to the start of the supported list, so it can look
* for matching ones during the handshake
*/
new_wsi->a.protocol = vhost->protocols;
new_wsi->user_space = NULL;
/*
* outermost create notification for wsi
* no user_space because no protocol selection
*/
vhost->protocols[0].callback(new_wsi, LWS_CALLBACK_WSI_CREATE, NULL,
NULL, 0);
return new_wsi;
}
/* if not a socket, it's a raw, non-ssl file descriptor
* req cx lock, acq pt lock, acq vh lock
*/
static struct lws *
__lws_adopt_descriptor_vhost1(struct lws_vhost *vh, lws_adoption_type type,
const char *vh_prot_name, struct lws *parent,
void *opaque, const char *fi_wsi_name)
{
struct lws_context *context;
struct lws_context_per_thread *pt;
struct lws *new_wsi;
int n;
/*
* Notice that in SMP case, the wsi may be being created on an
* entirely different pt / tsi for load balancing. In that case as
* we initialize it, it may become "live" concurrently unexpectedly...
*/
if (!vh)
return NULL;
context = vh->context;
lws_context_assert_lock_held(vh->context);
n = -1;
if (parent)
n = parent->tsi;
new_wsi = lws_create_new_server_wsi(vh, n, LWSLCG_WSI_SERVER, fi_wsi_name);
if (!new_wsi)
return NULL;
/* bring in specific fault injection rules early */
lws_fi_inherit_copy(&new_wsi->fic, &context->fic, "wsi", fi_wsi_name);
if (lws_fi(&new_wsi->fic, "createfail")) {
lws_fi_destroy(&new_wsi->fic);
return NULL;
}
new_wsi->a.opaque_user_data = opaque;
pt = &context->pt[(int)new_wsi->tsi];
lws_pt_lock(pt, __func__);
if (parent) {
new_wsi->parent = parent;
new_wsi->sibling_list = parent->child_list;
parent->child_list = new_wsi;
}
if (vh_prot_name) {
new_wsi->a.protocol = lws_vhost_name_to_protocol(new_wsi->a.vhost,
vh_prot_name);
if (!new_wsi->a.protocol) {
lwsl_vhost_err(new_wsi->a.vhost, "Protocol %s not enabled",
vh_prot_name);
goto bail;
}
if (lws_ensure_user_space(new_wsi)) {
lwsl_wsi_notice(new_wsi, "OOM");
goto bail;
}
}
if (!LWS_SSL_ENABLED(new_wsi->a.vhost) ||
!(type & LWS_ADOPT_SOCKET))
type &= (unsigned int)~LWS_ADOPT_ALLOW_SSL;
if (lws_role_call_adoption_bind(new_wsi, (int)type, vh_prot_name)) {
lwsl_wsi_err(new_wsi, "no role for desc type 0x%x", type);
goto bail;
}
#if defined(LWS_WITH_SERVER)
if (new_wsi->role_ops) {
lws_metrics_tag_wsi_add(new_wsi, "role", new_wsi->role_ops->name);
}
#endif
lws_pt_unlock(pt);
/*
* he's an allocated wsi, but he's not on any fds list or child list,
* join him to the vhost's list of these kinds of incomplete wsi until
* he gets another identity (he may do async dns now...)
*/
lws_vhost_lock(new_wsi->a.vhost);
lws_dll2_add_head(&new_wsi->vh_awaiting_socket,
&new_wsi->a.vhost->vh_awaiting_socket_owner);
lws_vhost_unlock(new_wsi->a.vhost);
return new_wsi;
bail:
lwsl_wsi_notice(new_wsi, "exiting on bail");
if (parent)
parent->child_list = new_wsi->sibling_list;
if (new_wsi->user_space)
lws_free(new_wsi->user_space);
lws_fi_destroy(&new_wsi->fic);
lws_pt_unlock(pt);
__lws_vhost_unbind_wsi(new_wsi); /* req cx, acq vh lock */
lws_free(new_wsi);
return NULL;
}
#if defined(LWS_WITH_SERVER) && defined(LWS_WITH_SECURE_STREAMS)
/*
* If the incoming wsi is bound to a vhost that is a ss server, this creates
* an accepted ss bound to the wsi.
*
* For h1 or raw, we can do the binding here, but for muxed protocols like h2
* or mqtt we have to do it not on the nwsi but on the stream. And for h2 we
* start off bound to h1 role, since we don't know if we will upgrade to h2
* until we meet the server.
*
* 1) No tls is assumed to mean no muxed protocol so can do it at adopt.
*
* 2) After alpn if not muxed we can do it.
*
* 3) For muxed, do it at the nwsi migration and on new stream
*/
int
lws_adopt_ss_server_accept(struct lws *new_wsi)
{
struct lws_context_per_thread *pt =
&new_wsi->a.context->pt[(int)new_wsi->tsi];
lws_ss_handle_t *h;
void *pv, **ppv;
if (!new_wsi->a.vhost->ss_handle)
return 0;
pv = (char *)&new_wsi->a.vhost->ss_handle[1];
/*
* Yes... the vhost is pointing to its secure stream representing the
* server... we want to create an accepted SS and bind it to new_wsi,
* the info/ssi from the server SS (so the SS callbacks defined there),
* the opaque_user_data of the server object and the policy of it.
*/
ppv = (void **)((char *)pv +
new_wsi->a.vhost->ss_handle->info.opaque_user_data_offset);
/*
* indicate we are an accepted connection referencing the
* server object
*/
new_wsi->a.vhost->ss_handle->info.flags |= LWSSSINFLAGS_SERVER;
if (lws_ss_create(new_wsi->a.context, new_wsi->tsi,
&new_wsi->a.vhost->ss_handle->info,
*ppv, &h, NULL, NULL)) {
lwsl_wsi_err(new_wsi, "accept ss creation failed");
goto fail1;
}
/*
* We made a fresh accepted SS conn from the server pieces,
* now bind the wsi... the problem is, this is the nwsi if it's
* h2.
*/
h->wsi = new_wsi;
new_wsi->a.opaque_user_data = h;
h->info.flags |= LWSSSINFLAGS_ACCEPTED;
/* indicate wsi should invalidate any ss link to it on close */
new_wsi->for_ss = 1;
// lwsl_wsi_notice(new_wsi, "%s: opaq %p, role %s",
// new_wsi->a.opaque_user_data,
// new_wsi->role_ops->name);
h->policy = new_wsi->a.vhost->ss_handle->policy;
/* apply requested socket options */
if (lws_plat_set_socket_options_ip(new_wsi->desc.sockfd,
h->policy->priority,
(LCCSCF_IP_LOW_LATENCY *
!!(h->policy->flags & LWSSSPOLF_ATTR_LOW_LATENCY)) |
(LCCSCF_IP_HIGH_THROUGHPUT *
!!(h->policy->flags & LWSSSPOLF_ATTR_HIGH_THROUGHPUT)) |
(LCCSCF_IP_HIGH_RELIABILITY *
!!(h->policy->flags & LWSSSPOLF_ATTR_HIGH_RELIABILITY)) |
(LCCSCF_IP_LOW_COST *
!!(h->policy->flags & LWSSSPOLF_ATTR_LOW_COST))))
lwsl_wsi_warn(new_wsi, "unable to set ip options");
/*
* add us to the list of clients that came in from the server
*/
lws_pt_lock(pt, __func__);
lws_dll2_add_tail(&h->cli_list, &new_wsi->a.vhost->ss_handle->src_list);
lws_pt_unlock(pt);
/*
* Let's give it appropriate state notifications
*/
if (lws_ss_event_helper(h, LWSSSCS_CREATING))
goto fail;
if (lws_ss_event_helper(h, LWSSSCS_CONNECTING))
goto fail;
/* defer CONNECTED until we see if he is upgrading */
// if (lws_ss_event_helper(h, LWSSSCS_CONNECTED))
// goto fail;
// lwsl_notice("%s: accepted ss complete, pcol %s\n", __func__,
// new_wsi->a.protocol->name);
return 0;
fail:
lws_ss_destroy(&h);
fail1:
return 1;
}
#endif
static struct lws *
lws_adopt_descriptor_vhost2(struct lws *new_wsi, lws_adoption_type type,
lws_sock_file_fd_type fd)
{
struct lws_context_per_thread *pt =
&new_wsi->a.context->pt[(int)new_wsi->tsi];
int n;
/* enforce that every fd is nonblocking */
if (type & LWS_ADOPT_SOCKET) {
if (lws_plat_set_nonblocking(fd.sockfd)) {
lwsl_wsi_err(new_wsi, "unable to set sockfd %d nonblocking",
fd.sockfd);
goto fail;
}
}
#if !defined(WIN32)
else
if (lws_plat_set_nonblocking(fd.filefd)) {
lwsl_wsi_err(new_wsi, "unable to set filefd nonblocking");
goto fail;
}
#endif
new_wsi->desc = fd;
if (!LWS_SSL_ENABLED(new_wsi->a.vhost) ||
!(type & LWS_ADOPT_SOCKET))
type &= (unsigned int)~LWS_ADOPT_ALLOW_SSL;
/*
* A new connection was accepted. Give the user a chance to
* set properties of the newly created wsi. There's no protocol
* selected yet so we issue this to the vhosts's default protocol,
* itself by default protocols[0]
*/
new_wsi->wsistate |= LWSIFR_SERVER;
n = LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED;
if (new_wsi->role_ops->adoption_cb[lwsi_role_server(new_wsi)])
n = new_wsi->role_ops->adoption_cb[lwsi_role_server(new_wsi)];
if (new_wsi->a.context->event_loop_ops->sock_accept)
if (new_wsi->a.context->event_loop_ops->sock_accept(new_wsi))
goto fail;
#if LWS_MAX_SMP > 1
/*
* Caution: after this point the wsi is live on its service thread
* which may be concurrent to this. We mark the wsi as still undergoing
* init in another pt so the assigned pt leaves it alone.
*/
new_wsi->undergoing_init_from_other_pt = 1;
#endif
if (!(type & LWS_ADOPT_ALLOW_SSL)) {
lws_pt_lock(pt, __func__);
if (__insert_wsi_socket_into_fds(new_wsi->a.context, new_wsi)) {
lws_pt_unlock(pt);
lwsl_wsi_err(new_wsi, "fail inserting socket");
goto fail;
}
lws_pt_unlock(pt);
}
#if defined(LWS_WITH_SERVER)
else
if (lws_server_socket_service_ssl(new_wsi, fd.sockfd, 0)) {
lwsl_wsi_info(new_wsi, "fail ssl negotiation");
goto fail;
}
#endif
lws_vhost_lock(new_wsi->a.vhost);
/* he has fds visibility now, remove from vhost orphan list */
lws_dll2_remove(&new_wsi->vh_awaiting_socket);
lws_vhost_unlock(new_wsi->a.vhost);
/*
* by deferring callback to this point, after insertion to fds,
* lws_callback_on_writable() can work from the callback
*/
if ((new_wsi->a.protocol->callback)(new_wsi, (enum lws_callback_reasons)n, new_wsi->user_space,
NULL, 0))
goto fail;
/* role may need to do something after all adoption completed */
lws_role_call_adoption_bind(new_wsi, (int)type | _LWS_ADOPT_FINISH,
new_wsi->a.protocol->name);
#if defined(LWS_WITH_SERVER) && defined(LWS_WITH_SECURE_STREAMS)
/*
* Did we come from an accepted client connection to a ss server?
*
* !!! For mux protocols, this will cause an additional inactive ss
* representing the nwsi. Doing that allows us to support both h1
* (here) and h2 (at __lws_wsi_server_new())
*/
lwsl_wsi_info(new_wsi, "vhost %s", new_wsi->a.vhost->lc.gutag);
if (lws_adopt_ss_server_accept(new_wsi))
goto fail;
#endif
#if LWS_MAX_SMP > 1
/* its actual pt can service it now */
new_wsi->undergoing_init_from_other_pt = 0;
#endif
lws_cancel_service_pt(new_wsi);
return new_wsi;
fail:
if (type & LWS_ADOPT_SOCKET)
lws_close_free_wsi(new_wsi, LWS_CLOSE_STATUS_NOSTATUS,
"adopt skt fail");
return NULL;
}
/* if not a socket, it's a raw, non-ssl file descriptor */
struct lws *
lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
lws_sock_file_fd_type fd, const char *vh_prot_name,
struct lws *parent)
{
lws_adopt_desc_t info;
memset(&info, 0, sizeof(info));
info.vh = vh;
info.type = type;
info.fd = fd;
info.vh_prot_name = vh_prot_name;
info.parent = parent;
return lws_adopt_descriptor_vhost_via_info(&info);
}
struct lws *
lws_adopt_descriptor_vhost_via_info(const lws_adopt_desc_t *info)
{
socklen_t slen = sizeof(lws_sockaddr46);
struct lws *new_wsi;
#if defined(LWS_WITH_PEER_LIMITS)
struct lws_peer *peer = NULL;
if (info->type & LWS_ADOPT_SOCKET) {
peer = lws_get_or_create_peer(info->vh, info->fd.sockfd);
if (peer && info->vh->context->ip_limit_wsi &&
peer->count_wsi >= info->vh->context->ip_limit_wsi) {
lwsl_info("Peer reached wsi limit %d\n",
info->vh->context->ip_limit_wsi);
if (info->vh->context->pl_notify_cb)
info->vh->context->pl_notify_cb(
info->vh->context,
info->fd.sockfd,
&peer->sa46);
compatible_close(info->fd.sockfd);
return NULL;
}
}
#endif
lws_context_lock(info->vh->context, __func__);
new_wsi = __lws_adopt_descriptor_vhost1(info->vh, info->type,
info->vh_prot_name, info->parent,
info->opaque, info->fi_wsi_name);
if (!new_wsi) {
if (info->type & LWS_ADOPT_SOCKET)
compatible_close(info->fd.sockfd);
goto bail;
}
if (info->type & LWS_ADOPT_SOCKET &&
getpeername(info->fd.sockfd, (struct sockaddr *)&new_wsi->sa46_peer,
&slen) < 0)
lwsl_info("%s: getpeername failed\n", __func__);
#if defined(LWS_WITH_PEER_LIMITS)
if (peer)
lws_peer_add_wsi(info->vh->context, peer, new_wsi);
#endif
new_wsi = lws_adopt_descriptor_vhost2(new_wsi, info->type, info->fd);
bail:
lws_context_unlock(info->vh->context);
return new_wsi;
}
struct lws *
lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd)
{
lws_sock_file_fd_type fd;
fd.sockfd = accept_fd;
return lws_adopt_descriptor_vhost(vh, LWS_ADOPT_SOCKET |
LWS_ADOPT_HTTP | LWS_ADOPT_ALLOW_SSL, fd, NULL, NULL);
}
struct lws *
lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd)
{
return lws_adopt_socket_vhost(context->vhost_list, accept_fd);
}
/* Common read-buffer adoption for lws_adopt_*_readbuf */
static struct lws*
adopt_socket_readbuf(struct lws *wsi, const char *readbuf, size_t len)
{
struct lws_context_per_thread *pt;
struct lws_pollfd *pfd;
int n;
if (!wsi)
return NULL;
if (!readbuf || len == 0)
return wsi;
if (wsi->position_in_fds_table == LWS_NO_FDS_POS)
return wsi;
pt = &wsi->a.context->pt[(int)wsi->tsi];
n = lws_buflist_append_segment(&wsi->buflist, (const uint8_t *)readbuf,
len);
if (n < 0)
goto bail;
if (n)
lws_dll2_add_head(&wsi->dll_buflist, &pt->dll_buflist_owner);
/*
* we can't process the initial read data until we can attach an ah.
*
* if one is available, get it and place the data in his ah rxbuf...
* wsi with ah that have pending rxbuf get auto-POLLIN service.
*
* no autoservice because we didn't get a chance to attach the
* readbuf data to wsi or ah yet, and we will do it next if we get
* the ah.
*/
if (wsi->http.ah || !lws_header_table_attach(wsi, 0)) {
lwsl_notice("%s: calling service on readbuf ah\n", __func__);
/*
* unlike a normal connect, we have the headers already
* (or the first part of them anyway).
* libuv won't come back and service us without a network
* event, so we need to do the header service right here.
*/
pfd = &pt->fds[wsi->position_in_fds_table];
pfd->revents |= LWS_POLLIN;
lwsl_err("%s: calling service\n", __func__);
if (lws_service_fd_tsi(wsi->a.context, pfd, wsi->tsi))
/* service closed us */
return NULL;
return wsi;
}
lwsl_err("%s: deferring handling ah\n", __func__);
return wsi;
bail:
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
"adopt skt readbuf fail");
return NULL;
}
#if defined(LWS_WITH_UDP)
#if defined(LWS_WITH_CLIENT)
/*
* This is the ASYNC_DNS callback target for udp client, it's analogous to
* connect3()
*/
static struct lws *
lws_create_adopt_udp2(struct lws *wsi, const char *ads,
const struct addrinfo *r, int n, void *opaque)
{
lws_sock_file_fd_type sock;
int bc = 1, m;
assert(wsi);
if (ads && (n < 0 || !r)) {
/*
* DNS lookup failed: there are no usable results. Fail the
* overall connection request.
*/
lwsl_notice("%s: bad: n %d, r %p\n", __func__, n, r);
goto bail;
}
m = lws_sort_dns(wsi, r);
#if defined(LWS_WITH_SYS_ASYNC_DNS)
lws_async_dns_freeaddrinfo(&r);
#else
freeaddrinfo((struct addrinfo *)r);
#endif
if (m)
goto bail;
while (lws_dll2_get_head(&wsi->dns_sorted_list)) {
lws_dns_sort_t *s = lws_container_of(
lws_dll2_get_head(&wsi->dns_sorted_list),
lws_dns_sort_t, list);
/*
* Remove it from the head, but don't free it yet... we are
* taking responsibility to free it
*/
lws_dll2_remove(&s->list);
/*
* We have done the dns lookup, identify the result we want
* if any, and then complete the adoption by binding wsi to
* socket opened on it.
*
* Ignore the weak assumptions about protocol driven by port
* number and force to DGRAM / UDP since that's what this
* function is for.
*/
#if !defined(__linux__)
sock.sockfd = socket(s->dest.sa4.sin_family,
SOCK_DGRAM, IPPROTO_UDP);
#else
/* PF_PACKET is linux-only */
sock.sockfd = socket(wsi->pf_packet ? PF_PACKET :
s->dest.sa4.sin_family,
SOCK_DGRAM, wsi->pf_packet ?
htons(0x800) : IPPROTO_UDP);
#endif
if (sock.sockfd == LWS_SOCK_INVALID)
goto resume;
/* ipv6 udp!!! */
if (s->af == AF_INET)
s->dest.sa4.sin_port = htons(wsi->c_port);
#if defined(LWS_WITH_IPV6)
else
s->dest.sa6.sin6_port = htons(wsi->c_port);
#endif
if (setsockopt(sock.sockfd, SOL_SOCKET, SO_REUSEADDR,
(const char *)&bc, sizeof(bc)) < 0)
lwsl_err("%s: failed to set reuse\n", __func__);
if (wsi->do_broadcast &&
setsockopt(sock.sockfd, SOL_SOCKET, SO_BROADCAST,
(const char *)&bc, sizeof(bc)) < 0)
lwsl_err("%s: failed to set broadcast\n", __func__);
/* Bind the udp socket to a particular network interface */
if (opaque &&
lws_plat_BINDTODEVICE(sock.sockfd, (const char *)opaque))
goto resume;
if (wsi->do_bind &&
bind(sock.sockfd, sa46_sockaddr(&s->dest),
#if defined(_WIN32)
(int)sa46_socklen(&s->dest)
#else
sizeof(struct sockaddr)
#endif
) == -1) {
lwsl_err("%s: bind failed\n", __func__);
goto resume;
}
if (!wsi->do_bind && !wsi->pf_packet) {
#if !defined(__APPLE__)
if (connect(sock.sockfd, sa46_sockaddr(&s->dest),
sa46_socklen(&s->dest)) == -1 &&
errno != EADDRNOTAVAIL /* openbsd */ ) {
lwsl_err("%s: conn fd %d fam %d %s:%u failed "
"errno %d\n", __func__, sock.sockfd,
s->dest.sa4.sin_family,
ads ? ads : "null", wsi->c_port,
LWS_ERRNO);
compatible_close(sock.sockfd);
goto resume;
}
#endif
}
if (wsi->udp)
wsi->udp->sa46 = s->dest;
wsi->sa46_peer = s->dest;
/* we connected: complete the udp socket adoption flow */
#if defined(LWS_WITH_SYS_ASYNC_DNS)
{
lws_async_dns_server_t *asds =
__lws_async_dns_server_find_wsi(
&wsi->a.context->async_dns, wsi);
if (asds)
asds->dns_server_connected = 1;
}
#endif
lws_free(s);
lws_addrinfo_clean(wsi);
return lws_adopt_descriptor_vhost2(wsi,
LWS_ADOPT_RAW_SOCKET_UDP, sock);
resume:
lws_free(s);
}
lwsl_err("%s: unable to create INET socket %d\n", __func__, LWS_ERRNO);
lws_addrinfo_clean(wsi);
#if defined(LWS_WITH_SYS_ASYNC_DNS)
{
lws_async_dns_server_t *asds = __lws_async_dns_server_find_wsi(
&wsi->a.context->async_dns, wsi);
if (asds)
lws_async_dns_drop_server(asds);
}
#endif
bail:
/* caller must close */
return NULL;
}
struct lws *
lws_create_adopt_udp(struct lws_vhost *vhost, const char *ads, int port,
int flags, const char *protocol_name, const char *ifname,
struct lws *parent_wsi, void *opaque,
const lws_retry_bo_t *retry_policy, const char *fi_wsi_name)
{
#if !defined(LWS_PLAT_OPTEE)
struct lws *wsi;
int n;
lwsl_info("%s: %s:%u\n", __func__, ads ? ads : "null", port);
/* create the logical wsi without any valid fd */
lws_context_lock(vhost->context, __func__);
wsi = __lws_adopt_descriptor_vhost1(vhost, LWS_ADOPT_SOCKET |
LWS_ADOPT_RAW_SOCKET_UDP,
protocol_name, parent_wsi, opaque,
fi_wsi_name);
lws_context_unlock(vhost->context);
if (!wsi) {
lwsl_err("%s: udp wsi creation failed\n", __func__);
goto bail;
}
// lwsl_notice("%s: role %s\n", __func__, wsi->role_ops->name);
wsi->do_bind = !!(flags & LWS_CAUDP_BIND);
wsi->do_broadcast = !!(flags & LWS_CAUDP_BROADCAST);
wsi->pf_packet = !!(flags & LWS_CAUDP_PF_PACKET);
wsi->c_port = (uint16_t)(unsigned int)port;
if (retry_policy)
wsi->retry_policy = retry_policy;
else
wsi->retry_policy = vhost->retry_policy;
#if !defined(LWS_WITH_SYS_ASYNC_DNS)
{
struct addrinfo *r, h;
char buf[16];
memset(&h, 0, sizeof(h));
h.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
h.ai_socktype = SOCK_DGRAM;
h.ai_protocol = IPPROTO_UDP;
#if defined(AI_PASSIVE)
h.ai_flags = AI_PASSIVE;
#endif
#ifdef AI_ADDRCONFIG
h.ai_flags |= AI_ADDRCONFIG;
#endif
/* if the dns lookup is synchronous, do the whole thing now */
lws_snprintf(buf, sizeof(buf), "%u", port);
n = getaddrinfo(ads, buf, &h, &r);
if (n) {
#if !defined(LWS_PLAT_FREERTOS)
lwsl_cx_info(vhost->context, "getaddrinfo error: %d", n);
#else
#if (_LWS_ENABLED_LOGS & LLL_INFO)
char t16[16];
lwsl_cx_info(vhost->context, "getaddrinfo error: %s",
lws_errno_describe(LWS_ERRNO, t16, sizeof(t16)));
#endif
#endif
//freeaddrinfo(r);
goto bail1;
}
/*
* With synchronous dns, complete it immediately after the
* blocking dns lookup finished... free r when connect either
* completed or failed
*/
wsi = lws_create_adopt_udp2(wsi, ads, r, 0, NULL);
return wsi;
}
#else
if (ads) {
/*
* with async dns, use the wsi as the point about which to do
* the dns lookup and have it call the second part when it's
* done.
*
* Keep a refcount on the results and free it when we connected
* or definitively failed.
*
* Notice wsi has no socket at this point (we don't know what
* kind to ask for until we get the dns back). But it is bound
* to a vhost and can be cleaned up from that at vhost destroy.
*/
n = lws_async_dns_query(vhost->context, 0, ads,
LWS_ADNS_RECORD_A,
lws_create_adopt_udp2, wsi,
(void *)ifname, NULL);
// lwsl_notice("%s: dns query returned %d\n", __func__, n);
if (n == LADNS_RET_FAILED) {
lwsl_err("%s: async dns failed\n", __func__);
wsi = NULL;
/*
* It was already closed by calling callback with error
* from lws_async_dns_query()
*/
goto bail;
}
} else {
lwsl_debug("%s: udp adopt has no ads\n", __func__);
wsi = lws_create_adopt_udp2(wsi, ads, NULL, 0, (void *)ifname);
}
/* dns lookup is happening asynchronously */
// lwsl_notice("%s: returning wsi %p\n", __func__, wsi);
return wsi;
#endif
#if !defined(LWS_WITH_SYS_ASYNC_DNS)
bail1:
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "adopt udp2 fail");
wsi = NULL;
#endif
bail:
return wsi;
#else
return NULL;
#endif
}
#endif
#endif
struct lws *
lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd,
const char *readbuf, size_t len)
{
return adopt_socket_readbuf(lws_adopt_socket(context, accept_fd),
readbuf, len);
}
struct lws *
lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost,
lws_sockfd_type accept_fd,
const char *readbuf, size_t len)
{
return adopt_socket_readbuf(lws_adopt_socket_vhost(vhost, accept_fd),
readbuf, len);
}

View File

@ -0,0 +1,121 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* 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 "private-lib-core.h"
#if defined(LWS_CLIENT_HTTP_PROXYING)
int
lws_set_proxy(struct lws_vhost *vhost, const char *proxy)
{
char authstring[96];
int brackets = 0;
char *p;
if (!proxy)
return -1;
/* we have to deal with a possible redundant leading http:// */
if (!strncmp(proxy, "http://", 7))
proxy += 7;
p = strrchr(proxy, '@');
if (p) { /* auth is around */
if (lws_ptr_diff_size_t(p, proxy) > sizeof(authstring) - 1)
goto auth_too_long;
lws_strncpy(authstring, proxy, lws_ptr_diff_size_t(p, proxy) + 1);
// null termination not needed on input
if (lws_b64_encode_string(authstring, lws_ptr_diff(p, proxy),
vhost->proxy_basic_auth_token,
sizeof vhost->proxy_basic_auth_token) < 0)
goto auth_too_long;
lwsl_vhost_info(vhost, " Proxy auth in use");
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
proxy = p + 1;
#endif
} else
vhost->proxy_basic_auth_token[0] = '\0';
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
#if defined(LWS_WITH_IPV6)
/*
* isolating the address / port is complicated by IPv6 overloading
* the meaning of : in the address. The convention to solve it is to
* put [] around the ipv6 address part, eg, "[::1]:443". This must be
* parsed to "::1" as the address and the port as 443.
*
* IPv4 addresses like myproxy:443 continue to be parsed as normal.
*/
if (proxy[0] == '[')
brackets = 1;
#endif
lws_strncpy(vhost->http.http_proxy_address, proxy + brackets,
sizeof(vhost->http.http_proxy_address));
p = vhost->http.http_proxy_address;
#if defined(LWS_WITH_IPV6)
if (brackets) {
/* original is IPv6 format "[::1]:443" */
p = strchr(vhost->http.http_proxy_address, ']');
if (!p) {
lwsl_vhost_err(vhost, "malformed proxy '%s'", proxy);
return -1;
}
*p++ = '\0';
}
#endif
p = strchr(p, ':');
if (!p && !vhost->http.http_proxy_port) {
lwsl_vhost_err(vhost, "http_proxy needs to be ads:port");
return -1;
}
if (p) {
*p = '\0';
vhost->http.http_proxy_port = (unsigned int)atoi(p + 1);
}
lwsl_vhost_info(vhost, " Proxy %s:%u", vhost->http.http_proxy_address,
vhost->http.http_proxy_port);
#endif
return 0;
auth_too_long:
lwsl_vhost_err(vhost, "proxy auth too long");
return -1;
}
#endif

View File

@ -0,0 +1,155 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2019 - 2021 Andy Green <andy@warmcat.com>
*
* 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.
*
* Client Connection Latency and DNS reporting
*/
/*
* We want to allocate copies for and append DNS results that we don't already
* have. We take this approach because a) we may be getting duplicated results
* from multiple DNS servers, and b) we may be getting results stacatto over
* time.
*
* We capture DNS results from either getaddrinfo or ASYNC_DNS the same here,
* before they are sorted and filtered.
*
* Because this is relatively expensive, we only do it on client wsi that
* explicitly indicated that they want it with the LCCSCF_CONMON flag.
*/
#include <private-lib-core.h>
int
lws_conmon_append_copy_new_dns_results(struct lws *wsi,
const struct addrinfo *cai)
{
if (!(wsi->flags & LCCSCF_CONMON))
return 0;
/*
* Let's go through the incoming guys, seeing if we already have them,
* or if we want to take a copy
*/
while (cai) {
struct addrinfo *ai = wsi->conmon.dns_results_copy;
char skip = 0;
/* do we already have this guy? */
while (ai) {
if (ai->ai_family != cai->ai_family &&
ai->ai_addrlen != cai->ai_addrlen &&
ai->ai_protocol != cai->ai_protocol &&
ai->ai_socktype != cai->ai_socktype &&
/* either ipv4 or v6 address must match */
((ai->ai_family == AF_INET &&
((struct sockaddr_in *)ai->ai_addr)->
sin_addr.s_addr ==
((struct sockaddr_in *)cai->ai_addr)->
sin_addr.s_addr)
#if defined(LWS_WITH_IPV6)
||
(ai->ai_family == AF_INET6 &&
!memcmp(((struct sockaddr_in6 *)ai->ai_addr)->
sin6_addr.s6_addr,
((struct sockaddr_in6 *)cai->ai_addr)->
sin6_addr.s6_addr, 16))
#endif
)) {
/* yes, we already got a copy then */
skip = 1;
break;
}
ai = ai->ai_next;
}
if (!skip) {
/*
* No we don't already have a copy of this one, let's
* allocate and append it then
*/
size_t al = sizeof(struct addrinfo) +
(size_t)cai->ai_addrlen;
size_t cl = cai->ai_canonname ?
strlen(cai->ai_canonname) + 1 : 0;
ai = lws_malloc(al + cl + 1, __func__);
if (!ai) {
lwsl_wsi_warn(wsi, "OOM");
return 1;
}
*ai = *cai;
ai->ai_addr = (struct sockaddr *)&ai[1];
memcpy(ai->ai_addr, cai->ai_addr, (size_t)cai->ai_addrlen);
if (cl) {
ai->ai_canonname = ((char *)ai->ai_addr) +
cai->ai_addrlen;
memcpy(ai->ai_canonname, cai->ai_canonname,
cl);
ai->ai_canonname[cl] = '\0';
}
ai->ai_next = wsi->conmon.dns_results_copy;
wsi->conmon.dns_results_copy = ai;
}
cai = cai->ai_next;
}
return 0;
}
void
lws_conmon_addrinfo_destroy(struct addrinfo *ai)
{
while (ai) {
struct addrinfo *ai1 = ai->ai_next;
lws_free(ai);
ai = ai1;
}
}
void
lws_conmon_wsi_take(struct lws *wsi, struct lws_conmon *dest)
{
memcpy(dest, &wsi->conmon, sizeof(*dest));
dest->peer46 = wsi->sa46_peer;
/* wsi no longer has to free it... */
wsi->conmon.dns_results_copy = NULL;
wsi->perf_done = 1;
}
void
lws_conmon_release(struct lws_conmon *conmon)
{
if (!conmon)
return;
lws_conmon_addrinfo_destroy(conmon->dns_results_copy);
conmon->dns_results_copy = NULL;
}

View File

@ -0,0 +1,560 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* 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 "private-lib-core.h"
static const uint8_t hnames[] = {
_WSI_TOKEN_CLIENT_PEER_ADDRESS,
_WSI_TOKEN_CLIENT_URI,
_WSI_TOKEN_CLIENT_HOST,
_WSI_TOKEN_CLIENT_ORIGIN,
_WSI_TOKEN_CLIENT_SENT_PROTOCOLS,
_WSI_TOKEN_CLIENT_METHOD,
_WSI_TOKEN_CLIENT_IFACE,
_WSI_TOKEN_CLIENT_ALPN
};
struct lws *
lws_http_client_connect_via_info2(struct lws *wsi)
{
struct client_info_stash *stash = wsi->stash;
int n;
lwsl_wsi_debug(wsi, "stash %p", stash);
if (!stash)
return wsi;
wsi->a.opaque_user_data = wsi->stash->opaque_user_data;
if (stash->cis[CIS_METHOD] && (!strcmp(stash->cis[CIS_METHOD], "RAW") ||
!strcmp(stash->cis[CIS_METHOD], "MQTT")))
goto no_ah;
/*
* we're not necessarily in a position to action these right away,
* stash them... we only need during connect phase so into a temp
* allocated stash
*/
for (n = 0; n < (int)LWS_ARRAY_SIZE(hnames); n++)
if (hnames[n] && stash->cis[n] &&
lws_hdr_simple_create(wsi, hnames[n], stash->cis[n]))
goto bail;
#if defined(LWS_WITH_SOCKS5)
if (!wsi->a.vhost->socks_proxy_port)
lws_free_set_NULL(wsi->stash);
#endif
no_ah:
return lws_client_connect_2_dnsreq(wsi);
bail:
#if defined(LWS_WITH_SOCKS5)
if (!wsi->a.vhost->socks_proxy_port)
lws_free_set_NULL(wsi->stash);
#endif
lws_free_set_NULL(wsi->stash);
return NULL;
}
int
lws_client_stash_create(struct lws *wsi, const char **cisin)
{
size_t size;
char *pc;
int n;
size = sizeof(*wsi->stash) + 1;
/*
* Let's overallocate the stash object with space for all the args
* in one hit.
*/
for (n = 0; n < CIS_COUNT; n++)
if (cisin[n])
size += strlen(cisin[n]) + 1;
if (wsi->stash)
lws_free_set_NULL(wsi->stash);
wsi->stash = lws_malloc(size, "client stash");
if (!wsi->stash)
return 1;
/* all the pointers default to NULL, but no need to zero the args */
memset(wsi->stash, 0, sizeof(*wsi->stash));
pc = (char *)&wsi->stash[1];
for (n = 0; n < CIS_COUNT; n++)
if (cisin[n]) {
size_t mm;
wsi->stash->cis[n] = pc;
if (n == CIS_PATH && cisin[n][0] != '/')
*pc++ = '/';
mm = strlen(cisin[n]) + 1;
memcpy(pc, cisin[n], mm);
pc += mm;
}
return 0;
}
struct lws *
lws_client_connect_via_info(const struct lws_client_connect_info *i)
{
const char *local = i->protocol;
struct lws *wsi, *safe = NULL;
const struct lws_protocols *p;
const char *cisin[CIS_COUNT];
char buf_localport[8];
struct lws_vhost *vh;
int tsi;
if (i->context->requested_stop_internal_loops)
return NULL;
if (!i->context->protocol_init_done)
if (lws_protocol_init(i->context))
return NULL;
/*
* If we have .local_protocol_name, use it to select the local protocol
* handler to bind to. Otherwise use .protocol if http[s].
*/
if (i->local_protocol_name)
local = i->local_protocol_name;
lws_context_lock(i->context, __func__);
/*
* PHASE 1: if SMP, find out the tsi related to current service thread
*/
tsi = lws_pthread_self_to_tsi(i->context);
assert(tsi >= 0);
/* PHASE 2: create a bare wsi */
wsi = __lws_wsi_create_with_role(i->context, tsi, NULL, i->log_cx);
lws_context_unlock(i->context);
if (wsi == NULL)
return NULL;
vh = i->vhost;
if (!vh) {
#if defined(LWS_WITH_TLS_JIT_TRUST)
if (lws_tls_jit_trust_vhost_bind(i->context, i->address, &vh))
#endif
{
vh = lws_get_vhost_by_name(i->context, "default");
if (!vh) {
vh = i->context->vhost_list;
if (!vh) { /* coverity */
lwsl_cx_err(i->context, "no vhost");
goto bail;
}
if (!strcmp(vh->name, "system"))
vh = vh->vhost_next;
}
}
}
#if defined(LWS_WITH_SECURE_STREAMS)
/* any of these imply we are a client wsi bound to an SS, which
* implies our opaque user ptr is the ss (or sspc if PROXY_LINK) handle
*/
wsi->for_ss = !!(i->ssl_connection & (LCCSCF_SECSTREAM_CLIENT | LCCSCF_SECSTREAM_PROXY_LINK | LCCSCF_SECSTREAM_PROXY_ONWARD));
wsi->client_bound_sspc = !!(i->ssl_connection & LCCSCF_SECSTREAM_PROXY_LINK); /* so wsi close understands need to remove sspc ptr to wsi */
wsi->client_proxy_onward = !!(i->ssl_connection & LCCSCF_SECSTREAM_PROXY_ONWARD);
#endif
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
wsi->fic.name = "wsi";
if (i->fic.fi_owner.count)
/*
* This moves all the lws_fi_t from i->fi to the vhost fi,
* leaving it empty
*/
lws_fi_import(&wsi->fic, &i->fic);
lws_fi_inherit_copy(&wsi->fic, &i->context->fic, "wsi", i->fi_wsi_name);
if (lws_fi(&wsi->fic, "createfail"))
goto bail;
#if defined(LWS_WITH_SECURE_STREAMS)
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
if (wsi->client_bound_sspc) {
lws_sspc_handle_t *fih = (lws_sspc_handle_t *)i->opaque_user_data;
lws_fi_inherit_copy(&wsi->fic, &fih->fic, "wsi", NULL);
}
#endif
if (wsi->for_ss) {
lws_ss_handle_t *fih = (lws_ss_handle_t *)i->opaque_user_data;
lws_fi_inherit_copy(&wsi->fic, &fih->fic, "wsi", NULL);
}
#endif
#endif
lws_wsi_fault_timedclose(wsi);
/*
* Until we exit, we can report connection failure directly to the
* caller without needing to call through to protocol CONNECTION_ERROR.
*/
wsi->client_suppress_CONNECTION_ERROR = 1;
if (i->keep_warm_secs)
wsi->keep_warm_secs = i->keep_warm_secs;
else
wsi->keep_warm_secs = 5;
wsi->flags = i->ssl_connection;
wsi->c_pri = i->priority;
if (i->retry_and_idle_policy)
wsi->retry_policy = i->retry_and_idle_policy;
else
wsi->retry_policy = &i->context->default_retry;
if (i->ssl_connection & LCCSCF_WAKE_SUSPEND__VALIDITY)
wsi->conn_validity_wakesuspend = 1;
lws_vhost_bind_wsi(vh, wsi);
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
/* additionally inerit from vhost we bound to */
lws_fi_inherit_copy(&wsi->fic, &vh->fic, "wsi", i->fi_wsi_name);
#endif
if (!wsi->a.vhost) {
lwsl_wsi_err(wsi, "No vhost in the context");
goto bail;
}
/*
* PHASE 3: Choose an initial role for the wsi and do role-specific init
*
* Note the initial role may not reflect the final role, eg,
* we may want ws, but first we have to go through h1 to get that
*/
if (lws_role_call_client_bind(wsi, i) < 0) {
lwsl_wsi_err(wsi, "unable to bind to role");
goto bail;
}
lwsl_wsi_info(wsi, "role binding to %s", wsi->role_ops->name);
/*
* PHASE 4: fill up the wsi with stuff from the connect_info as far as
* it can go. It's uncertain because not only is our connection
* going to complete asynchronously, we might have bound to h1 and not
* even be able to get ahold of an ah immediately.
*/
wsi->user_space = NULL;
wsi->pending_timeout = NO_PENDING_TIMEOUT;
wsi->position_in_fds_table = LWS_NO_FDS_POS;
wsi->ocport = wsi->c_port = (uint16_t)(unsigned int)i->port;
wsi->sys_tls_client_cert = i->sys_tls_client_cert;
#if defined(LWS_ROLE_H2)
wsi->txc.manual_initial_tx_credit =
(int32_t)i->manual_initial_tx_credit;
#endif
wsi->a.protocol = &wsi->a.vhost->protocols[0];
wsi->client_pipeline = !!(i->ssl_connection & LCCSCF_PIPELINE);
wsi->client_no_follow_redirect = !!(i->ssl_connection &
LCCSCF_HTTP_NO_FOLLOW_REDIRECT);
/*
* PHASE 5: handle external user_space now, generic alloc is done in
* role finalization
*/
if (i->userdata) {
wsi->user_space_externally_allocated = 1;
wsi->user_space = i->userdata;
}
if (local) {
lwsl_wsi_info(wsi, "vh %s protocol binding to %s\n",
wsi->a.vhost->name, local);
p = lws_vhost_name_to_protocol(wsi->a.vhost, local);
if (p)
lws_bind_protocol(wsi, p, __func__);
else
lwsl_wsi_info(wsi, "unknown protocol %s", local);
lwsl_wsi_info(wsi, "%s: %s %s entry",
lws_wsi_tag(wsi), wsi->role_ops->name,
wsi->a.protocol ? wsi->a.protocol->name : "none");
}
/*
* PHASE 5: handle external user_space now, generic alloc is done in
* role finalization
*/
if (!wsi->user_space && i->userdata) {
wsi->user_space_externally_allocated = 1;
wsi->user_space = i->userdata;
}
#if defined(LWS_WITH_TLS)
wsi->tls.use_ssl = (unsigned int)i->ssl_connection;
#else
if (i->ssl_connection & LCCSCF_USE_SSL) {
lwsl_wsi_err(wsi, "lws not configured for tls");
goto bail;
}
#endif
/*
* PHASE 6: stash the things from connect_info that we can't process
* right now, eg, if http binding, without an ah. If h1 and no ah, we
* will go on the ah waiting list and process those things later (after
* the connect_info and maybe the things pointed to have gone out of
* scope)
*
* However these things are stashed in a generic way at this point,
* with no relationship to http or ah
*/
cisin[CIS_ADDRESS] = i->address;
cisin[CIS_PATH] = i->path;
cisin[CIS_HOST] = i->host;
cisin[CIS_ORIGIN] = i->origin;
cisin[CIS_PROTOCOL] = i->protocol;
cisin[CIS_METHOD] = i->method;
cisin[CIS_IFACE] = i->iface;
lws_snprintf(buf_localport, sizeof(buf_localport), "%u", i->local_port);
cisin[CIS_LOCALPORT] = buf_localport;
cisin[CIS_ALPN] = i->alpn;
cisin[CIS_USERNAME] = i->auth_username;
cisin[CIS_PASSWORD] = i->auth_password;
if (lws_client_stash_create(wsi, cisin))
goto bail;
#if defined(LWS_WITH_TLS)
if (i->alpn)
lws_strncpy(wsi->alpn, i->alpn, sizeof(wsi->alpn));
#endif
wsi->a.opaque_user_data = wsi->stash->opaque_user_data =
i->opaque_user_data;
#if defined(LWS_WITH_SECURE_STREAMS)
if (wsi->for_ss) {
/* it's related to ss... the options are
*
* LCCSCF_SECSTREAM_PROXY_LINK : client SSPC link to proxy
* LCCSCF_SECSTREAM_PROXY_ONWARD: proxy's onward connection
*/
__lws_lc_tag(i->context, &i->context->lcg[
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
i->ssl_connection & LCCSCF_SECSTREAM_PROXY_LINK ? LWSLCG_WSI_SSP_CLIENT :
#if defined(LWS_WITH_SERVER)
(i->ssl_connection & LCCSCF_SECSTREAM_PROXY_ONWARD ? LWSLCG_WSI_SSP_ONWARD :
#endif
LWSLCG_WSI_CLIENT
#if defined(LWS_WITH_SERVER)
)
#endif
],
#else
LWSLCG_WSI_CLIENT],
#endif
&wsi->lc, "%s/%s/%s/(%s)", i->method ? i->method : "WS",
wsi->role_ops->name, i->address,
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
wsi->client_bound_sspc ?
lws_sspc_tag((lws_sspc_handle_t *)i->opaque_user_data) :
#endif
lws_ss_tag(((lws_ss_handle_t *)i->opaque_user_data)));
} else
#endif
__lws_lc_tag(i->context, &i->context->lcg[LWSLCG_WSI_CLIENT], &wsi->lc,
"%s/%s/%s/%s", i->method ? i->method : "WS",
wsi->role_ops->name ? wsi->role_ops->name : "novh", vh->name, i->address);
lws_metrics_tag_wsi_add(wsi, "vh", wsi->a.vhost->name);
/*
* at this point user callbacks like
* LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER will be interested to
* know the parent... eg for proxying we can grab extra headers from
* the parent's incoming ah and add them to the child client handshake
*/
if (i->parent_wsi) {
lwsl_wsi_info(wsi, "created as child %s",
lws_wsi_tag(i->parent_wsi));
wsi->parent = i->parent_wsi;
safe = wsi->sibling_list = i->parent_wsi->child_list;
i->parent_wsi->child_list = wsi;
}
/*
* PHASE 7: Do any role-specific finalization processing. We can still
* see important info things via wsi->stash
*/
if (lws_rops_fidx(wsi->role_ops, LWS_ROPS_client_bind)) {
int n = lws_rops_func_fidx(wsi->role_ops, LWS_ROPS_client_bind).
client_bind(wsi, NULL);
if (n && i->parent_wsi)
/* unpick from parent */
i->parent_wsi->child_list = safe;
if (n < 0)
/* we didn't survive, wsi is freed */
goto bail2;
if (n)
/* something else failed, wsi needs freeing */
goto bail;
}
/* let the caller's optional wsi storage have the wsi we created */
if (i->pwsi)
*i->pwsi = wsi;
if (!wsi->a.protocol)
/* we must have one protocol or another bound by this point */
goto bail;
/* PHASE 8: notify protocol with role-specific connected callback */
/* raw socket per se doesn't want this... raw socket proxy wants it... */
if (wsi->role_ops != &role_ops_raw_skt ||
(i->local_protocol_name &&
!strcmp(i->local_protocol_name, "raw-proxy"))) {
lwsl_wsi_debug(wsi, "adoption cb %d to %s %s",
wsi->role_ops->adoption_cb[0],
wsi->role_ops->name, wsi->a.protocol->name);
wsi->a.protocol->callback(wsi, wsi->role_ops->adoption_cb[0],
wsi->user_space, NULL, 0);
}
#if defined(LWS_WITH_HUBBUB)
if (i->uri_replace_to)
wsi->http.rw = lws_rewrite_create(wsi, html_parser_cb,
i->uri_replace_from,
i->uri_replace_to);
#endif
if (i->method && (!strcmp(i->method, "RAW") // ||
// !strcmp(i->method, "MQTT")
)) {
/*
* Not for MQTT here, since we don't know if we will
* pipeline it or not...
*/
#if defined(LWS_WITH_TLS)
wsi->tls.ssl = NULL;
if (wsi->role_ops != &role_ops_raw_skt && (wsi->tls.use_ssl & LCCSCF_USE_SSL)) {
const char *cce = NULL;
switch (
#if !defined(LWS_WITH_SYS_ASYNC_DNS)
lws_client_create_tls(wsi, &cce, 1)
#else
lws_client_create_tls(wsi, &cce, 0)
#endif
) {
case 1:
return wsi;
case 0:
break;
default:
goto bail3;
}
}
#endif
/* fallthru */
wsi = lws_http_client_connect_via_info2(wsi);
}
if (wsi)
/*
* If it subsequently fails, report CONNECTION_ERROR,
* because we're going to return a non-error return now.
*/
wsi->client_suppress_CONNECTION_ERROR = 0;
return wsi;
#if defined(LWS_WITH_TLS)
bail3:
lwsl_wsi_info(wsi, "tls start fail");
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "tls start fail");
if (i->pwsi)
*i->pwsi = NULL;
return NULL;
#endif
bail:
#if defined(LWS_WITH_TLS)
if (wsi->tls.ssl)
lws_tls_restrict_return(wsi);
#endif
lws_free_set_NULL(wsi->stash);
lws_fi_destroy(&wsi->fic);
lws_free(wsi);
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
bail2:
#endif
if (i->pwsi)
*i->pwsi = NULL;
return NULL;
}

View File

@ -0,0 +1,395 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* 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 "private-lib-core.h"
#if !defined(WIN32)
#include <netdb.h>
#endif
#ifndef AI_V4MAPPED
#define AI_V4MAPPED 0
#endif
#if !defined(LWS_WITH_SYS_ASYNC_DNS)
static int
lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result)
{
lws_metrics_caliper_declare(cal, wsi->a.context->mt_conn_dns);
struct addrinfo hints;
#if defined(LWS_WITH_SYS_METRICS)
char buckname[32];
#endif
int n;
memset(&hints, 0, sizeof(hints));
*result = NULL;
hints.ai_socktype = SOCK_STREAM;
#ifdef LWS_WITH_IPV6
if (wsi->ipv6) {
#if !defined(__ANDROID__)
hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_V4MAPPED;
#endif
} else
#endif
{
hints.ai_family = PF_UNSPEC;
}
#if defined(LWS_WITH_CONMON)
wsi->conmon_datum = lws_now_usecs();
#endif
wsi->dns_reachability = 0;
if (lws_fi(&wsi->fic, "dnsfail"))
n = EAI_FAIL;
else
n = getaddrinfo(ads, NULL, &hints, result);
#if defined(LWS_WITH_CONMON)
wsi->conmon.ciu_dns = (lws_conmon_interval_us_t)
(lws_now_usecs() - wsi->conmon_datum);
#endif
/*
* Which EAI_* are available and the meanings are highly platform-
* dependent, even different linux distros differ.
*/
if (0
#if defined(EAI_SYSTEM)
|| n == EAI_SYSTEM
#endif
#if defined(EAI_NODATA)
|| n == EAI_NODATA
#endif
#if defined(EAI_FAIL)
|| n == EAI_FAIL
#endif
#if defined(EAI_AGAIN)
|| n == EAI_AGAIN
#endif
) {
wsi->dns_reachability = 1;
lws_metrics_caliper_report(cal, METRES_NOGO);
#if defined(LWS_WITH_SYS_METRICS)
lws_snprintf(buckname, sizeof(buckname), "dns=\"unreachable %d\"", n);
lws_metrics_hist_bump_priv_wsi(wsi, mth_conn_failures, buckname);
#endif
#if defined(LWS_WITH_CONMON)
wsi->conmon.dns_disposition = LWSCONMON_DNS_SERVER_UNREACHABLE;
#endif
#if 0
lwsl_wsi_debug(wsi, "asking to recheck CPD in 1s");
lws_system_cpd_start_defer(wsi->a.context, LWS_US_PER_SEC);
#endif
}
lwsl_wsi_info(wsi, "getaddrinfo '%s' says %d", ads, n);
#if defined(LWS_WITH_SYS_METRICS)
if (n < 0) {
lws_snprintf(buckname, sizeof(buckname), "dns=\"nores %d\"", n);
lws_metrics_hist_bump_priv_wsi(wsi, mth_conn_failures, buckname);
}
#endif
#if defined(LWS_WITH_CONMON)
wsi->conmon.dns_disposition = n < 0 ? LWSCONMON_DNS_NO_RESULT :
LWSCONMON_DNS_OK;
#endif
lws_metrics_caliper_report(cal, n >= 0 ? METRES_GO : METRES_NOGO);
return n;
}
#endif
#if !defined(LWS_WITH_SYS_ASYNC_DNS) && defined(EAI_NONAME)
static const char * const dns_nxdomain = "DNS NXDOMAIN";
#endif
struct lws *
lws_client_connect_2_dnsreq(struct lws *wsi)
{
struct addrinfo *result = NULL;
const char *meth = NULL;
#if defined(LWS_WITH_IPV6)
struct sockaddr_in addr;
const char *iface;
#endif
const char *adsin;
int n, port = 0;
struct lws *w;
if (lwsi_state(wsi) == LRS_WAITING_DNS ||
lwsi_state(wsi) == LRS_WAITING_CONNECT) {
lwsl_wsi_info(wsi, "LRS_WAITING_DNS / CONNECT");
return wsi;
}
/*
* clients who will create their own fresh connection keep a copy of
* the hostname they originally connected to, in case other connections
* want to use it too
*/
if (!wsi->cli_hostname_copy) {
const char *pa = lws_wsi_client_stash_item(wsi, CIS_HOST,
_WSI_TOKEN_CLIENT_PEER_ADDRESS);
if (pa)
wsi->cli_hostname_copy = lws_strdup(pa);
}
/*
* The first job is figure out if we want to pipeline on or just join
* an existing "active connection" to the same place
*/
meth = lws_wsi_client_stash_item(wsi, CIS_METHOD,
_WSI_TOKEN_CLIENT_METHOD);
/* consult active connections to find out disposition */
adsin = lws_wsi_client_stash_item(wsi, CIS_ADDRESS,
_WSI_TOKEN_CLIENT_PEER_ADDRESS);
/* we only pipeline connections that said it was okay */
if (!wsi->client_pipeline) {
lwsl_wsi_debug(wsi, "new conn on no pipeline flag");
goto solo;
}
if (wsi->keepalive_rejected) {
lwsl_notice("defeating pipelining due to no "
"keepalive on server\n");
goto solo;
}
/* only pipeline things we associate with being a stream */
if (meth && !_lws_is_http_method(meth) && strcmp(meth, "RAW") &&
strcmp(meth, "UDP") && strcmp(meth, "MQTT"))
goto solo;
if (!adsin)
/*
* This cannot happen since user code must provide the client
* address to get this far, it's here to satisfy Coverity
*/
return NULL;
switch (lws_vhost_active_conns(wsi, &w, adsin)) {
case ACTIVE_CONNS_SOLO:
break;
case ACTIVE_CONNS_MUXED:
lwsl_wsi_notice(wsi, "ACTIVE_CONNS_MUXED");
if (lwsi_role_h2(wsi)) {
if (wsi->a.protocol->callback(wsi,
LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP,
wsi->user_space, NULL, 0))
goto failed1;
//lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2);
//lwsi_set_state(w, LRS_ESTABLISHED);
lws_callback_on_writable(wsi);
}
return wsi;
case ACTIVE_CONNS_QUEUED:
lwsl_wsi_debug(wsi, "ACTIVE_CONNS_QUEUED st 0x%x: ",
lwsi_state(wsi));
if (lwsi_state(wsi) == LRS_UNCONNECTED) {
if (lwsi_role_h2(w))
lwsi_set_state(wsi,
LRS_H2_WAITING_TO_SEND_HEADERS);
else
lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2);
}
return lws_client_connect_4_established(wsi, w, 0);
}
solo:
/*
* If we made our own connection, and we're doing a method that can
* take a pipeline, we are an "active client connection".
*
* Add ourselves to the vhost list of those so that others can
* piggyback on our transaction queue
*/
if (meth && (!strcmp(meth, "RAW") || _lws_is_http_method(meth) ||
!strcmp(meth, "MQTT")) &&
lws_dll2_is_detached(&wsi->dll2_cli_txn_queue) &&
lws_dll2_is_detached(&wsi->dll_cli_active_conns)) {
lws_context_lock(wsi->a.context, __func__);
lws_vhost_lock(wsi->a.vhost);
lwsl_wsi_info(wsi, "adding as active conn");
/* caution... we will have to unpick this on oom4 path */
lws_dll2_add_head(&wsi->dll_cli_active_conns,
&wsi->a.vhost->dll_cli_active_conns_owner);
lws_vhost_unlock(wsi->a.vhost);
lws_context_unlock(wsi->a.context);
}
/*
* Since address must be given at client creation, should not be
* possible, but necessary to satisfy coverity
*/
if (!adsin)
return NULL;
#if defined(LWS_WITH_UNIX_SOCK)
/*
* unix socket destination?
*/
if (*adsin == '+') {
wsi->unix_skt = 1;
n = 0;
goto next_step;
}
#endif
/*
* start off allowing ipv6 on connection if vhost allows it
*/
wsi->ipv6 = LWS_IPV6_ENABLED(wsi->a.vhost);
#ifdef LWS_WITH_IPV6
if (wsi->stash)
iface = wsi->stash->cis[CIS_IFACE];
else
iface = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE);
if (wsi->ipv6 && iface &&
inet_pton(AF_INET, iface, &addr.sin_addr) == 1) {
lwsl_wsi_notice(wsi, "client connection forced to IPv4");
wsi->ipv6 = 0;
}
#endif
#if defined(LWS_CLIENT_HTTP_PROXYING) && \
(defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2))
/* Decide what it is we need to connect to:
*
* Priority 1: connect to http proxy */
if (wsi->a.vhost->http.http_proxy_port) {
adsin = wsi->a.vhost->http.http_proxy_address;
port = (int)wsi->a.vhost->http.http_proxy_port;
#else
if (0) {
#endif
#if defined(LWS_WITH_SOCKS5)
/* Priority 2: Connect to SOCK5 Proxy */
} else if (wsi->a.vhost->socks_proxy_port) {
lwsl_wsi_client(wsi, "Sending SOCKS Greeting");
adsin = wsi->a.vhost->socks_proxy_address;
port = (int)wsi->a.vhost->socks_proxy_port;
#endif
} else {
/* Priority 3: Connect directly */
/* ads already set */
port = wsi->c_port;
}
/*
* prepare the actual connection
* to whatever we decided to connect to
*/
lwsi_set_state(wsi, LRS_WAITING_DNS);
lwsl_wsi_info(wsi, "lookup %s:%u", adsin, port);
wsi->conn_port = (uint16_t)port;
#if !defined(LWS_WITH_SYS_ASYNC_DNS)
n = 0;
if (!wsi->dns_sorted_list.count) {
/*
* blocking dns resolution
*/
n = lws_getaddrinfo46(wsi, adsin, &result);
#if defined(EAI_NONAME)
if (n == EAI_NONAME) {
/*
* The DNS server responded with NXDOMAIN... even
* though this is still in the client creation call,
* we need to make a CCE, otherwise there won't be
* any user indication of what went wrong
*/
wsi->client_suppress_CONNECTION_ERROR = 0;
lws_inform_client_conn_fail(wsi, (void *)dns_nxdomain,
strlen(dns_nxdomain));
goto failed1;
}
#endif
}
#else
/* this is either FAILED, CONTINUING, or already called connect_4 */
if (lws_fi(&wsi->fic, "dnsfail"))
return lws_client_connect_3_connect(wsi, NULL, NULL, -4, NULL);
else
n = lws_async_dns_query(wsi->a.context, wsi->tsi, adsin,
LWS_ADNS_RECORD_A, lws_client_connect_3_connect,
wsi, NULL, NULL);
if (n == LADNS_RET_FAILED_WSI_CLOSED)
return NULL;
if (n == LADNS_RET_FAILED)
goto failed1;
return wsi;
#endif
#if defined(LWS_WITH_UNIX_SOCK)
next_step:
#endif
return lws_client_connect_3_connect(wsi, adsin, result, n, NULL);
//#if defined(LWS_WITH_SYS_ASYNC_DNS)
failed1:
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "client_connect2");
return NULL;
//#endif
}

View File

@ -0,0 +1,808 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* 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 "private-lib-core.h"
#if defined(WIN32)
/*
* Windows doesn't offer a Posix connect() event... we use a sul
* to check the connection status periodically while a connection
* is ongoing.
*
* Leaving this to POLLOUT to retry which is the way for Posix
* platforms instead on win32 causes event-loop busywaiting
* so for win32 we manage the retry interval directly with the sul.
*/
void
lws_client_win32_conn_async_check(lws_sorted_usec_list_t *sul)
{
struct lws *wsi = lws_container_of(sul, struct lws,
win32_sul_connect_async_check);
lwsl_wsi_debug(wsi, "checking ongoing connection attempt");
lws_client_connect_3_connect(wsi, NULL, NULL, 0, NULL);
}
#endif
void
lws_client_conn_wait_timeout(lws_sorted_usec_list_t *sul)
{
struct lws *wsi = lws_container_of(sul, struct lws,
sul_connect_timeout);
/*
* This is used to constrain the time we're willing to wait for a
* connection before giving up on it and retrying.
*/
lwsl_wsi_info(wsi, "connect wait timeout has fired");
lws_client_connect_3_connect(wsi, NULL, NULL, 0, NULL);
}
void
lws_client_dns_retry_timeout(lws_sorted_usec_list_t *sul)
{
struct lws *wsi = lws_container_of(sul, struct lws,
sul_connect_timeout);
/*
* This limits the amount of dns lookups we will try before
* giving up and failing... it reuses sul_connect_timeout, which
* isn't officially used until we connected somewhere.
*/
lwsl_wsi_info(wsi, "dns retry");
if (!lws_client_connect_2_dnsreq(wsi))
lwsl_wsi_notice(wsi, "DNS lookup failed");
}
/*
* Figure out if an ongoing connect() has arrived at a final disposition or not
*
* We can check using getsockopt if our connect actually completed.
* Posix connect() allows nonblocking to redo the connect to
* find out if it succeeded.
*/
typedef enum {
LCCCR_CONNECTED = 1,
LCCCR_CONTINUE = 0,
LCCCR_FAILED = -1,
} lcccr_t;
static lcccr_t
lws_client_connect_check(struct lws *wsi, int *real_errno)
{
#if !defined(LWS_WITH_NO_LOGS)
char t16[16];
#endif
int en = 0;
#if !defined(WIN32)
int e;
socklen_t sl = sizeof(e);
#endif
(void)en;
/*
* This resets SO_ERROR after reading it. If there's an error
* condition, the connect definitively failed.
*/
#if !defined(WIN32)
if (!getsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_ERROR, &e, &sl)) {
en = LWS_ERRNO;
if (!e) {
lwsl_wsi_debug(wsi, "getsockopt: conn OK errno %s",
lws_errno_describe(en, t16, sizeof(t16)));
return LCCCR_CONNECTED;
}
lwsl_wsi_notice(wsi, "getsockopt fd %d says %s", wsi->desc.sockfd,
lws_errno_describe(e, t16, sizeof(t16)));
*real_errno = e;
return LCCCR_FAILED;
}
#else
fd_set write_set, except_set;
struct timeval tv;
int ret;
FD_ZERO(&write_set);
FD_ZERO(&except_set);
FD_SET(wsi->desc.sockfd, &write_set);
FD_SET(wsi->desc.sockfd, &except_set);
tv.tv_sec = 0;
tv.tv_usec = 0;
ret = select((int)wsi->desc.sockfd + 1, NULL, &write_set, &except_set, &tv);
if (FD_ISSET(wsi->desc.sockfd, &write_set)) {
/* actually connected */
lwsl_wsi_debug(wsi, "select write fd set, conn OK");
return LCCCR_CONNECTED;
}
if (FD_ISSET(wsi->desc.sockfd, &except_set)) {
/* Failed to connect */
lwsl_wsi_notice(wsi, "connect failed, select exception fd set");
return LCCCR_FAILED;
}
if (!ret) {
lwsl_wsi_debug(wsi, "select timeout");
return LCCCR_CONTINUE;
}
en = LWS_ERRNO;
#endif
lwsl_wsi_notice(wsi, "connection check FAILED: %s",
lws_errno_describe(*real_errno || en, t16, sizeof(t16)));
return LCCCR_FAILED;
}
/*
* We come here to fire off a connect, and to check its disposition later.
*
* If it did not complete before the individual attempt timeout, we will try to
* connect again with the next dns result.
*/
struct lws *
lws_client_connect_3_connect(struct lws *wsi, const char *ads,
const struct addrinfo *result, int n, void *opaque)
{
#if defined(LWS_WITH_UNIX_SOCK)
struct sockaddr_un sau;
#endif
struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
const char *cce = "Unable to connect", *iface, *local_port;
const struct sockaddr *psa = NULL;
uint16_t port = wsi->conn_port;
char dcce[48], t16[16];
lws_dns_sort_t *curr;
ssize_t plen = 0;
lws_dll2_t *d;
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
int cfail;
#endif
int m, af = 0, en;
/*
* If we come here with result set, we need to convert getaddrinfo
* results to a lws_dns_sort_t list one time and free the results.
*
* We use this pattern because ASYNC_DNS will callback here with the
* results when it gets them (and may come here more than once, eg, for
* AAAA then A or vice-versa)
*/
if (result) {
lws_sul_cancel(&wsi->sul_connect_timeout);
#if defined(LWS_WITH_CONMON)
/* append a copy from before the sorting */
lws_conmon_append_copy_new_dns_results(wsi, result);
#endif
lws_sort_dns(wsi, result);
#if defined(LWS_WITH_SYS_ASYNC_DNS)
lws_async_dns_freeaddrinfo(&result);
#else
freeaddrinfo((struct addrinfo *)result);
#endif
result = NULL;
}
#if defined(LWS_WITH_UNIX_SOCK)
memset(&sau, 0, sizeof(sau));
#endif
/*
* async dns calls back here for everybody who cares when it gets a
* result... but if we are piggybacking, we do not want to connect
* ourselves
*/
if (!lws_dll2_is_detached(&wsi->dll2_cli_txn_queue))
return wsi;
if (n && /* calling back with a problem */
!wsi->dns_sorted_list.count && /* there's no results */
!lws_socket_is_valid(wsi->desc.sockfd) && /* no attempt ongoing */
!wsi->speculative_connect_owner.count /* no spec attempt */ ) {
lwsl_wsi_notice(wsi, "dns lookup failed %d", n);
/*
* DNS lookup itself failed... let's try again until we
* timeout
*/
lwsi_set_state(wsi, LRS_UNCONNECTED);
lws_sul_schedule(wsi->a.context, wsi->tsi, &wsi->sul_connect_timeout,
lws_client_dns_retry_timeout,
LWS_USEC_PER_SEC);
return wsi;
// cce = "dns lookup failed";
// goto oom4;
}
/*
* We come back here again when we think the connect() may have
* completed one way or the other, we can't proceed until we know we
* actually connected.
*/
if (lwsi_state(wsi) == LRS_WAITING_CONNECT &&
lws_socket_is_valid(wsi->desc.sockfd)) {
if (!wsi->dns_sorted_list.count &&
!wsi->sul_connect_timeout.list.owner)
/* no dns results and no ongoing timeout for one */
goto connect_to;
/*
* If the connection failed, the OS-level errno may be
* something like EINPROGRESS rather than the actual problem
* that prevented a connection. This value will represent the
* “real” problem that we should report to the caller.
*/
int real_errno = 0;
switch (lws_client_connect_check(wsi, &real_errno)) {
case LCCCR_CONNECTED:
/*
* Oh, it has happened...
*/
goto conn_good;
case LCCCR_CONTINUE:
#if defined(WIN32)
lws_sul_schedule(wsi->a.context, 0, &wsi->win32_sul_connect_async_check,
lws_client_win32_conn_async_check,
wsi->a.context->win32_connect_check_interval_usec);
#endif
return NULL;
default:
if (!real_errno)
real_errno = LWS_ERRNO;
lws_snprintf(dcce, sizeof(dcce), "conn fail: %s",
lws_errno_describe(real_errno, t16, sizeof(t16)));
cce = dcce;
lwsl_wsi_debug(wsi, "%s", dcce);
lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO);
goto try_next_dns_result_fds;
}
}
#if defined(LWS_WITH_UNIX_SOCK)
if (ads && *ads == '+') {
ads++;
memset(&wsi->sa46_peer, 0, sizeof(wsi->sa46_peer));
af = sau.sun_family = AF_UNIX;
strncpy(sau.sun_path, ads, sizeof(sau.sun_path));
sau.sun_path[sizeof(sau.sun_path) - 1] = '\0';
lwsl_wsi_info(wsi, "Unix skt: %s", ads);
if (sau.sun_path[0] == '@')
sau.sun_path[0] = '\0';
goto ads_known;
}
#endif
#if defined(LWS_WITH_SYS_ASYNC_DNS)
if (n == LADNS_RET_FAILED) {
lwsl_wsi_notice(wsi, "adns failed %s", ads);
/*
* Caller that is giving us LADNS_RET_FAILED will deal
* with cleanup
*/
return NULL;
}
#endif
/*
* Let's try directly connecting to each of the results in turn until
* one works, or we run out of results...
*
* We have a sorted dll2 list with the head one most preferable
*/
next_dns_result:
cce = "Unable to connect";
if (!wsi->dns_sorted_list.count)
goto failed1;
/*
* Copy the wsi head sorted dns result into the wsi->sa46_peer, and
* remove and free the original from the sorted list
*/
d = lws_dll2_get_head(&wsi->dns_sorted_list);
curr = lws_container_of(d, lws_dns_sort_t, list);
lws_dll2_remove(&curr->list);
wsi->sa46_peer = curr->dest;
#if defined(LWS_WITH_NETLINK)
wsi->peer_route_uidx = curr->uidx;
lwsl_wsi_info(wsi, "peer_route_uidx %d", wsi->peer_route_uidx);
#endif
lws_free(curr);
sa46_sockport(&wsi->sa46_peer, htons(port));
psa = sa46_sockaddr(&wsi->sa46_peer);
n = (int)sa46_socklen(&wsi->sa46_peer);
#if defined(LWS_WITH_UNIX_SOCK)
ads_known:
#endif
/*
* Now we prepared psa, if not already connecting, create the related
* socket and add to the fds
*/
if (!lws_socket_is_valid(wsi->desc.sockfd)) {
if (wsi->a.context->event_loop_ops->check_client_connect_ok &&
wsi->a.context->event_loop_ops->check_client_connect_ok(wsi)
) {
cce = "waiting for event loop watcher to close";
goto oom4;
}
#if defined(LWS_WITH_UNIX_SOCK)
af = 0;
if (wsi->unix_skt) {
af = AF_UNIX;
wsi->desc.sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
}
else
#endif
{
af = wsi->sa46_peer.sa4.sin_family;
wsi->desc.sockfd = socket(wsi->sa46_peer.sa4.sin_family,
SOCK_STREAM, 0);
}
if (!lws_socket_is_valid(wsi->desc.sockfd)) {
en = LWS_ERRNO;
lws_snprintf(dcce, sizeof(dcce),
"conn fail: skt creation: %s",
lws_errno_describe(en, t16, sizeof(t16)));
cce = dcce;
lwsl_wsi_warn(wsi, "%s", dcce);
goto try_next_dns_result;
}
if (lws_plat_set_socket_options(wsi->a.vhost, wsi->desc.sockfd,
#if defined(LWS_WITH_UNIX_SOCK)
wsi->unix_skt)) {
#else
0)) {
#endif
en = LWS_ERRNO;
lws_snprintf(dcce, sizeof(dcce),
"conn fail: skt options: %s",
lws_errno_describe(en, t16, sizeof(t16)));
cce = dcce;
lwsl_wsi_warn(wsi, "%s", dcce);
goto try_next_dns_result_closesock;
}
/* apply requested socket options */
if (lws_plat_set_socket_options_ip(wsi->desc.sockfd,
wsi->c_pri, wsi->flags))
lwsl_wsi_warn(wsi, "unable to set ip options");
lwsl_wsi_debug(wsi, "WAITING_CONNECT");
lwsi_set_state(wsi, LRS_WAITING_CONNECT);
if (wsi->a.context->event_loop_ops->sock_accept)
if (wsi->a.context->event_loop_ops->sock_accept(wsi)) {
lws_snprintf(dcce, sizeof(dcce),
"conn fail: sock accept");
cce = dcce;
lwsl_wsi_warn(wsi, "%s", dcce);
goto try_next_dns_result_closesock;
}
lws_pt_lock(pt, __func__);
if (__insert_wsi_socket_into_fds(wsi->a.context, wsi)) {
lws_snprintf(dcce, sizeof(dcce),
"conn fail: insert fd");
cce = dcce;
lws_pt_unlock(pt);
goto try_next_dns_result_closesock;
}
lws_pt_unlock(pt);
/*
* The fd + wsi combination is entered into the wsi tables
* at this point, with a pollfd
*
* Past here, we can't simply free the structs as error
* handling as oom4 does.
*
* We can run the whole close flow, or unpick the fds inclusion
* and anything else we have done.
*/
if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
lws_snprintf(dcce, sizeof(dcce),
"conn fail: change pollfd");
cce = dcce;
goto try_next_dns_result_fds;
}
if (!wsi->a.protocol)
wsi->a.protocol = &wsi->a.vhost->protocols[0];
lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE,
wsi->a.vhost->connect_timeout_secs);
iface = lws_wsi_client_stash_item(wsi, CIS_IFACE,
_WSI_TOKEN_CLIENT_IFACE);
local_port = lws_wsi_client_stash_item(wsi, CIS_LOCALPORT,
_WSI_TOKEN_CLIENT_LOCALPORT);
if ((iface && *iface) || (local_port && atoi(local_port))) {
m = lws_socket_bind(wsi->a.vhost, wsi, wsi->desc.sockfd,
(local_port ? atoi(local_port) : 0), iface, af);
if (m < 0) {
lws_snprintf(dcce, sizeof(dcce),
"conn fail: socket bind");
cce = dcce;
goto try_next_dns_result_fds;
}
}
}
#if defined(LWS_WITH_UNIX_SOCK)
if (wsi->unix_skt) {
psa = (const struct sockaddr *)&sau;
if (sau.sun_path[0])
n = (int)(sizeof(uint16_t) + strlen(sau.sun_path));
else
n = (int)(sizeof(uint16_t) +
strlen(&sau.sun_path[1]) + 1);
} else
#endif
if (!psa) /* coverity */
goto try_next_dns_result_fds;
/*
* The actual connection attempt
*/
#if defined(LWS_ESP_PLATFORM)
errno = 0;
#endif
/* grab a copy for peer tracking */
#if defined(LWS_WITH_UNIX_SOCK)
if (!wsi->unix_skt)
#endif
memmove(&wsi->sa46_peer, psa, (unsigned int)n);
/*
* Finally, make the actual connection attempt
*/
#if defined(LWS_WITH_SYS_METRICS)
if (wsi->cal_conn.mt) {
lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO);
}
lws_metrics_caliper_bind(wsi->cal_conn, wsi->a.context->mt_conn_tcp);
#endif
wsi->socket_is_permanently_unusable = 0;
if (lws_fi(&wsi->fic, "conn_cb_rej") ||
user_callback_handle_rxflow(wsi->a.protocol->callback, wsi,
LWS_CALLBACK_CONNECTING, wsi->user_space,
(void *)(intptr_t)wsi->desc.sockfd, 0)) {
lwsl_wsi_info(wsi, "CONNECTION CB closed");
goto failed1;
}
{
char buf[64];
lws_sa46_write_numeric_address((lws_sockaddr46 *)psa, buf, sizeof(buf));
lwsl_wsi_notice(wsi, "trying %s", buf);
}
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
cfail = lws_fi(&wsi->fic, "connfail");
if (cfail)
m = -1;
else
#endif
m = connect(wsi->desc.sockfd, (const struct sockaddr *)psa,
(socklen_t)n);
#if defined(LWS_WITH_CONMON)
wsi->conmon_datum = lws_now_usecs();
wsi->conmon.ciu_sockconn = 0;
#endif
if (m == -1) {
/*
* Since we're nonblocking, connect not having completed is not
* necessarily indicating any problem... we have to look at
* either errno or the socket to understand if we actually
* failed already...
*/
int errno_copy = LWS_ERRNO;
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
if (cfail)
/* fake an abnormal, fatal situation */
errno_copy = 999;
#endif
lwsl_wsi_debug(wsi, "connect: fd %d, %s",
wsi->desc.sockfd,
lws_errno_describe(errno_copy, t16, sizeof(t16)));
if (errno_copy &&
errno_copy != LWS_EALREADY &&
errno_copy != LWS_EINPROGRESS &&
errno_copy != LWS_EWOULDBLOCK
#ifdef _WIN32
&& errno_copy != WSAEINVAL
&& errno_copy != WSAEISCONN
#endif
) {
/*
* The connect() failed immediately...
*/
#if defined(LWS_WITH_CONMON)
wsi->conmon.ciu_sockconn = (lws_conmon_interval_us_t)
(lws_now_usecs() - wsi->conmon_datum);
#endif
lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO);
#if defined(_DEBUG)
#if defined(LWS_WITH_UNIX_SOCK)
if (!wsi->unix_skt) {
#endif
char nads[48];
lws_sa46_write_numeric_address(&wsi->sa46_peer, nads,
sizeof(nads));
lws_snprintf(dcce, sizeof(dcce),
"conn fail: %s: %s:%d",
lws_errno_describe(errno_copy, t16, sizeof(t16)),
nads, port);
cce = dcce;
wsi->sa46_peer.sa4.sin_family = 0;
lwsl_wsi_info(wsi, "%s", cce);
#if defined(LWS_WITH_UNIX_SOCK)
} else {
lws_snprintf(dcce, sizeof(dcce),
"conn fail: %s: UDS %s",
lws_errno_describe(errno_copy, t16, sizeof(t16)), ads);
cce = dcce;
}
#endif
#endif
goto try_next_dns_result_fds;
}
#if defined(WIN32)
if (lws_plat_check_connection_error(wsi))
goto try_next_dns_result_fds;
if (errno_copy == WSAEISCONN)
goto conn_good;
#endif
/*
* The connection attempt is ongoing asynchronously... let's set
* a specialized timeout for this connect attempt completion, it
* uses wsi->sul_connect_timeout just for this purpose
*/
lws_sul_schedule(wsi->a.context, wsi->tsi, &wsi->sul_connect_timeout,
lws_client_conn_wait_timeout,
wsi->a.context->timeout_secs *
LWS_USEC_PER_SEC);
#if defined(WIN32)
/*
* Windows is not properly POSIX, we have to manually schedule a
* callback to poll checking its status
*/
lws_sul_schedule(wsi->a.context, 0, &wsi->win32_sul_connect_async_check,
lws_client_win32_conn_async_check,
wsi->a.context->win32_connect_check_interval_usec
);
#else
/*
* POSIX platforms must do specifically a POLLOUT poll to hear
* about the connect completion as a POLLOUT event
*/
if (lws_change_pollfd(wsi, 0, LWS_POLLOUT))
goto try_next_dns_result_fds;
#endif
return wsi;
}
conn_good:
/*
* The connection has happened
*/
#if defined(LWS_WITH_CONMON)
wsi->conmon.ciu_sockconn = (lws_conmon_interval_us_t)
(lws_now_usecs() - wsi->conmon_datum);
#endif
#if !defined(LWS_PLAT_OPTEE)
{
socklen_t salen = sizeof(wsi->sa46_local);
#if defined(_DEBUG)
char buf[64];
#endif
if (getsockname((int)wsi->desc.sockfd,
(struct sockaddr *)&wsi->sa46_local,
&salen) == -1) {
en = LWS_ERRNO;
lwsl_warn("getsockname: %s\n", lws_errno_describe(en, t16, sizeof(t16)));
}
#if defined(_DEBUG)
#if defined(LWS_WITH_UNIX_SOCK)
if (wsi->unix_skt)
buf[0] = '\0';
else
#endif
lws_sa46_write_numeric_address(&wsi->sa46_local, buf, sizeof(buf));
lwsl_wsi_info(wsi, "source ads %s", buf);
#endif
}
#endif
lws_sul_cancel(&wsi->sul_connect_timeout);
#if defined(WIN32)
lws_sul_cancel(&wsi->win32_sul_connect_async_check);
#endif
lws_metrics_caliper_report(wsi->cal_conn, METRES_GO);
lws_addrinfo_clean(wsi);
if (wsi->a.protocol)
wsi->a.protocol->callback(wsi, LWS_CALLBACK_WSI_CREATE,
wsi->user_space, NULL, 0);
lwsl_wsi_debug(wsi, "going into connect_4");
return lws_client_connect_4_established(wsi, NULL, plen);
oom4:
/*
* We get here if we're trying to clean up a connection attempt that
* didn't make it as far as getting inserted into the wsi / fd tables
*/
if (lwsi_role_client(wsi) && wsi->a.protocol
/* && lwsi_state_est(wsi) */)
lws_inform_client_conn_fail(wsi,(void *)cce, strlen(cce));
/* take care that we might be inserted in fds already */
if (wsi->position_in_fds_table != LWS_NO_FDS_POS)
/* do the full wsi close flow */
goto failed1;
lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO);
/*
* We can't be an active client connection any more, if we thought
* that was what we were going to be doing. It should be if we are
* failing by oom4 path, we are still called by
* lws_client_connect_via_info() and will be returning NULL to that,
* so nobody else should have had a chance to queue on us.
*/
{
struct lws_vhost *vhost = wsi->a.vhost;
lws_sockfd_type sfd = wsi->desc.sockfd;
//lws_vhost_lock(vhost);
__lws_free_wsi(wsi); /* acquires vhost lock in wsi reset */
//lws_vhost_unlock(vhost);
sanity_assert_no_wsi_traces(vhost->context, wsi);
sanity_assert_no_sockfd_traces(vhost->context, sfd);
}
return NULL;
connect_to:
/*
* It looks like the sul_connect_timeout fired
*/
lwsl_wsi_info(wsi, "abandoning connect due to timeout");
try_next_dns_result_fds:
lws_pt_lock(pt, __func__);
__remove_wsi_socket_from_fds(wsi);
lws_pt_unlock(pt);
try_next_dns_result_closesock:
/*
* We are killing the socket but leaving
*/
compatible_close(wsi->desc.sockfd);
wsi->desc.sockfd = LWS_SOCK_INVALID;
try_next_dns_result:
lws_sul_cancel(&wsi->sul_connect_timeout);
#if defined(WIN32)
lws_sul_cancel(&wsi->win32_sul_connect_async_check);
#endif
if (lws_dll2_get_head(&wsi->dns_sorted_list))
goto next_dns_result;
lws_addrinfo_clean(wsi);
lws_inform_client_conn_fail(wsi, (void *)cce, strlen(cce));
failed1:
lws_sul_cancel(&wsi->sul_connect_timeout);
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "client_connect3");
return NULL;
}

View File

@ -0,0 +1,338 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* 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 "private-lib-core.h"
struct lws *
lws_client_connect_4_established(struct lws *wsi, struct lws *wsi_piggyback,
ssize_t plen)
{
#if defined(LWS_CLIENT_HTTP_PROXYING)
struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
#endif
const char *meth;
struct lws_pollfd pfd;
const char *cce = "";
int n, m, rawish = 0;
meth = lws_wsi_client_stash_item(wsi, CIS_METHOD,
_WSI_TOKEN_CLIENT_METHOD);
if (meth && (!strcmp(meth, "RAW")
#if defined(LWS_ROLE_MQTT)
|| !strcmp(meth, "MQTT")
#endif
))
rawish = 1;
if (wsi_piggyback)
goto send_hs;
#if defined(LWS_CLIENT_HTTP_PROXYING)
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
/* we are connected to server, or proxy */
/* http proxy */
if (wsi->a.vhost->http.http_proxy_port) {
const char *cpa;
cpa = lws_wsi_client_stash_item(wsi, CIS_ADDRESS,
_WSI_TOKEN_CLIENT_PEER_ADDRESS);
if (!cpa)
goto failed;
lwsl_wsi_info(wsi, "going via proxy");
plen = lws_snprintf((char *)pt->serv_buf, 256,
"CONNECT %s:%u HTTP/1.1\x0d\x0a"
"Host: %s:%u\x0d\x0a"
"User-agent: lws\x0d\x0a", cpa, wsi->ocport,
cpa, wsi->ocport);
#if defined(LWS_WITH_HTTP_BASIC_AUTH)
if (wsi->a.vhost->proxy_basic_auth_token[0])
plen += lws_snprintf((char *)pt->serv_buf + plen, 256,
"Proxy-authorization: basic %s\x0d\x0a",
wsi->a.vhost->proxy_basic_auth_token);
#endif
plen += lws_snprintf((char *)pt->serv_buf + plen, 5,
"\x0d\x0a");
/* lwsl_hexdump_notice(pt->serv_buf, plen); */
/*
* OK from now on we talk via the proxy, so connect to that
*/
if (wsi->stash)
wsi->stash->cis[CIS_ADDRESS] =
wsi->a.vhost->http.http_proxy_address;
else
if (lws_hdr_simple_create(wsi,
_WSI_TOKEN_CLIENT_PEER_ADDRESS,
wsi->a.vhost->http.http_proxy_address))
goto failed;
wsi->c_port = (uint16_t)wsi->a.vhost->http.http_proxy_port;
n = (int)send(wsi->desc.sockfd, (char *)pt->serv_buf,
(unsigned int)plen,
MSG_NOSIGNAL);
if (n < 0) {
lwsl_wsi_debug(wsi, "ERROR writing to proxy socket");
cce = "proxy write failed";
goto failed;
}
lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE,
(int)wsi->a.context->timeout_secs);
wsi->conn_port = wsi->c_port;
lwsi_set_state(wsi, LRS_WAITING_PROXY_REPLY);
return wsi;
}
#endif
#endif
/* coverity */
if (!wsi->a.protocol)
return NULL;
#if defined(LWS_WITH_SOCKS5)
if (lwsi_state(wsi) != LRS_ESTABLISHED)
switch (lws_socks5c_greet(wsi, &cce)) {
case -1:
goto failed;
case 1:
return wsi;
default:
break;
}
#endif
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
send_hs:
if (wsi_piggyback &&
!lws_dll2_is_detached(&wsi->dll2_cli_txn_queue)) {
/*
* We are pipelining on an already-established connection...
* we can skip tls establishment.
*
* Set these queued guys to a state where they won't actually
* send their headers until we decide later.
*/
lwsi_set_state(wsi, LRS_H2_WAITING_TO_SEND_HEADERS);
/*
* we can't send our headers directly, because they have to
* be sent when the parent is writeable. The parent will check
* for anybody on his client transaction queue that is in
* LRS_H1C_ISSUE_HANDSHAKE2, and let them write.
*
* If we are trying to do this too early, before the network
* connection has written his own headers, then it will just
* wait in the queue until it's possible to send them.
*/
lws_callback_on_writable(wsi_piggyback);
lwsl_wsi_info(wsi, "waiting to send hdrs (par state 0x%x)",
lwsi_state(wsi_piggyback));
} else {
lwsl_wsi_info(wsi, "%s %s client created own conn "
"(raw %d) vh %s st 0x%x",
wsi->role_ops->name, wsi->a.protocol->name, rawish,
wsi->a.vhost->name, lwsi_state(wsi));
/* we are making our own connection */
if (!rawish
#if defined(LWS_WITH_TLS)
// && (!(wsi->tls.use_ssl & LCCSCF_USE_SSL) || wsi->tls.ssl)
#endif
) {
if (lwsi_state(wsi) != LRS_H1C_ISSUE_HANDSHAKE2)
lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE);
} else {
/* for a method = "RAW" connection, this makes us
* established */
#if defined(LWS_WITH_TLS)// && !defined(LWS_WITH_MBEDTLS)
/* we have connected if we got here */
if (lwsi_state(wsi) == LRS_WAITING_CONNECT &&
(wsi->tls.use_ssl & LCCSCF_USE_SSL)) {
int result;
//lwsi_set_state(wsi, LRS_WAITING_SSL);
/*
* We can retry this... just cook the SSL BIO
* the first time
*/
result = lws_client_create_tls(wsi, &cce, 1);
switch (result) {
case CCTLS_RETURN_DONE:
break;
case CCTLS_RETURN_RETRY:
lwsl_wsi_debug(wsi, "create_tls RETRY");
return wsi;
default:
lwsl_wsi_debug(wsi, "create_tls FAIL");
goto failed;
}
/*
* We succeeded to negotiate a new client tls
* tunnel. If it's h2 alpn, we have arranged
* to send the h2 prefix and set our state to
* LRS_H2_WAITING_TO_SEND_HEADERS already.
*/
lwsl_wsi_notice(wsi, "tls established st 0x%x, "
"client_h2_alpn %d", lwsi_state(wsi),
wsi->client_h2_alpn);
if (lwsi_state(wsi) !=
LRS_H2_WAITING_TO_SEND_HEADERS)
lwsi_set_state(wsi,
LRS_H1C_ISSUE_HANDSHAKE2);
lws_set_timeout(wsi,
PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND,
(int)wsi->a.context->timeout_secs);
#if 0
/* ensure pollin enabled */
if (lws_change_pollfd(wsi, 0, LWS_POLLIN))
lwsl_wsi_notice(wsi,
"unable to set POLLIN");
#endif
goto provoke_service;
}
#endif
/* clear his established timeout */
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
m = wsi->role_ops->adoption_cb[0];
if (m) {
n = user_callback_handle_rxflow(
wsi->a.protocol->callback, wsi,
(enum lws_callback_reasons)m,
wsi->user_space, NULL, 0);
if (n < 0) {
lwsl_wsi_info(wsi, "RAW_PROXY_CLI_ADOPT err");
goto failed;
}
}
/* service.c pollout processing wants this */
wsi->hdr_parsing_completed = 1;
#if defined(LWS_ROLE_MQTT)
if (meth && !strcmp(meth, "MQTT")) {
#if defined(LWS_WITH_TLS)
if (wsi->tls.use_ssl & LCCSCF_USE_SSL) {
lwsi_set_state(wsi, LRS_WAITING_SSL);
return wsi;
}
#endif
lwsl_wsi_info(wsi, "settings LRS_MQTTC_IDLE");
lwsi_set_state(wsi, LRS_MQTTC_IDLE);
/*
* provoke service to issue the CONNECT
* directly.
*/
lws_set_timeout(wsi,
PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE,
(int)wsi->a.context->timeout_secs);
assert(lws_socket_is_valid(wsi->desc.sockfd));
pfd.fd = wsi->desc.sockfd;
pfd.events = LWS_POLLIN;
pfd.revents = LWS_POLLOUT;
lwsl_wsi_info(wsi, "going to service fd");
n = lws_service_fd_tsi(wsi->a.context, &pfd, wsi->tsi);
if (n < 0) {
cce = "first service failed";
goto failed;
}
if (n)
/* returns 1 on fail after close wsi */
return NULL;
return wsi;
}
#endif
lwsl_wsi_info(wsi, "setting ESTABLISHED");
lwsi_set_state(wsi, LRS_ESTABLISHED);
return wsi;
}
/*
* provoke service to issue the handshake directly.
*
* we need to do it this way because in the proxy case, this is
* the next state and executed only if and when we get a good
* proxy response inside the state machine... but notice in
* SSL case this may not have sent anything yet with 0 return,
* and won't until many retries from main loop. To stop that
* becoming endless, cover with a timeout.
*/
#if defined(LWS_WITH_TLS) //&& !defined(LWS_WITH_MBEDTLS)
provoke_service:
#endif
lws_set_timeout(wsi, PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE,
(int)wsi->a.context->timeout_secs);
assert(lws_socket_is_valid(wsi->desc.sockfd));
pfd.fd = wsi->desc.sockfd;
pfd.events = LWS_POLLIN;
pfd.revents = LWS_POLLIN;
n = lws_service_fd_tsi(wsi->a.context, &pfd, wsi->tsi);
if (n < 0) {
cce = "first service failed";
goto failed;
}
if (n) /* returns 1 on failure after closing wsi */
return NULL;
}
#endif
return wsi;
failed:
lws_inform_client_conn_fail(wsi, (void *)cce, strlen(cce));
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "client_connect4");
return NULL;
}

View File

@ -0,0 +1,778 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* 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.
*
*
* Either the libc getaddrinfo() or ASYNC_DNS provides a chain of addrinfo,
* we use lws_sort_dns() to convert it to an lws_dll2 of lws_dns_sort_t, after
* which the addrinfo results are freed.
*
* If the system has no routing table info (from, eg, NETLINK), then that's
* it the sorted results are bound to the wsi and used.
*
* If the system has routing table info, we study the routing table and the
* DNS results in order to sort the lws_dns_sort_t result linked-list into
* most desirable at the head, and strip results we can't see a way to route.
*/
#include "private-lib-core.h"
#if defined(__linux__)
#include <linux/if_addr.h>
#endif
#if defined(__FreeBSD__)
#include <net/if.h>
#include <netinet6/in6_var.h>
#endif
#if defined(LWS_WITH_IPV6) && defined(LWS_WITH_NETLINK)
/*
* RFC6724 default policy table
*
* Prefix Precedence Label
* ::1/128 50 0
* ::/0 40 1
* ::ffff:0:0/96 35 4 (override prec to 100 to prefer ipv4)
* 2002::/16 30 2
* 2001::/32 5 5
* fc00::/7 3 13
* ::/96 1 3
* fec0::/10 1 11
* 3ffe::/16 1 12
*
* implemented using offsets into a combined 40-byte table below
*/
static const uint8_t ma[] = {
/* 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
/* 16 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff,
/* 28 */ 0x20, 0x02,
/* 30 */ 0x20, 0x01, 0x00, 0x00,
/* 34 */ 0xfc, 0x00,
/* 36 */ 0xfe, 0xc0,
/* 38 */ 0x3f, 0xfe
};
static const uint8_t frac[] = {
0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe
};
/* 9 x 4 byte = 36 byte policy index table */
static const struct score_policy {
uint8_t ma_ofs;
uint8_t prefix;
lws_dns_score_t score;
} rfc6724_policy[] = {
{ 0, 128, { 50, 0 } }, /* ::1/128 */
{ 0, 0, { 40, 1 } }, /* ::0 */
#if 1
/* favour ipv6 as a general policy */
{ 16, 96, { 35, 4 } }, /* ::ffff:0:0/96 */
#else
/* favour ipv4 as a general policy */
{ 16, 96, { 100, 4 } }, /* ::ffff:0:0/96 */
#endif
{ 28, 16, { 30, 2 } }, /* 2002::/16 */
{ 30, 32, { 5, 5 } }, /* 2001::/32 */
{ 34, 7, { 3, 13 } }, /* fc00::/7 */
{ 0, 96, { 1, 3 } }, /* ::/96 */
{ 36, 10, { 1, 11 } }, /* fec0::/10 */
{ 38, 16, { 1, 12 } }, /* 3ffe::/16 */
};
static int
lws_ipv6_prefix_match_len(const struct sockaddr_in6 *a,
const struct sockaddr_in6 *b)
{
const uint8_t *ads_a = (uint8_t *)&a->sin6_addr,
*ads_b = (uint8_t *)&b->sin6_addr;
int n = 0, match = 0;
for (n = 0; n < 16; n++) {
if (ads_a[n] == ads_b[n])
match += 8;
else
break;
}
if (match != 128) {
int m;
for (m = 1; m < 8; m++) {
if ((ads_a[n] & frac[m]) == (ads_b[n] & frac[m]))
match++;
else
break;
}
}
return match;
}
static int
lws_ipv6_unicast_scope(const struct sockaddr_in6 *sa)
{
uint64_t *u;
u = (uint64_t *)&sa->sin6_addr;
if (*u == 0xfe80000000000000ull)
return 2; /* link-local */
return 0xe;
}
static int
lws_sort_dns_scope(lws_sockaddr46 *sa46)
{
if (sa46->sa4.sin_family == AF_INET) {
uint8_t *p = (uint8_t *)&sa46->sa4.sin_addr;
/* RFC6724 3.2 */
if (p[0] == 127 || (p[0] == 169 && p[1] == 254))
return 2; /* link-local */
return 0xe; /* global */
}
return lws_ipv6_unicast_scope(&sa46->sa6);
}
static int
lws_sort_dns_classify(lws_sockaddr46 *sa46, lws_dns_score_t *score)
{
const struct score_policy *pol = rfc6724_policy;
const uint8_t *p, *po;
lws_sockaddr46 s;
int n, m;
memset(score, 0, sizeof(*score));
if (sa46->sa4.sin_family == AF_INET) {
memset(&s, 0, sizeof(s));
s.sa6.sin6_family = AF_INET6;
lws_4to6((uint8_t *)s.sa6.sin6_addr.s6_addr,
(const uint8_t *)&sa46->sa4.sin_addr);
/* use the v6 version of the v4 address */
sa46 = &s;
}
for (n = 0; n < (int)LWS_ARRAY_SIZE(rfc6724_policy); n++) {
po = (uint8_t *)&sa46->sa6.sin6_addr.s6_addr;
p = &ma[pol->ma_ofs];
for (m = 0; m < pol->prefix >> 3; m++)
if (*p++ != *po++)
goto next;
if ((pol->prefix & 7) && (*p & frac[pol->prefix & 7]) !=
(*po & frac[pol->prefix & 7]))
goto next;
*score = pol->score;
return 0;
next:
pol++;
}
return 1;
}
enum {
SAS_PREFER_A = 1,
SAS_SAME = 0,
SAS_PREFER_B = -1
};
/* ifa is laid out with types for ipv4, if it's AF_INET6 case to sockaddr_in6 */
#define to_v6_sa(x) ((struct sockaddr_in6 *)x)
#define to_sa46_sa(x) ((lws_sockaddr46 *)x)
/*
* The source address selection algorithm produces as output a single
* source address for use with a given destination address. This
* algorithm only applies to IPv6 destination addresses, not IPv4
* addresses.
*
* This implements RFC6724 Section 5.
*
* Either or both sa and sb can be dest or gateway routes
*/
static int
lws_sort_dns_scomp(struct lws_context_per_thread *pt, const lws_route_t *sa,
const lws_route_t *sb, const struct sockaddr_in6 *dst)
{
const struct sockaddr_in6 *sa6 = to_v6_sa(&sa->dest),
*sb6 = to_v6_sa(&sb->dest);
lws_dns_score_t scorea, scoreb, scoredst;
int scopea, scopeb, scoped, mla, mlb;
lws_route_t *rd;
if (!sa->dest.sa4.sin_family)
sa6 = to_v6_sa(&sa->gateway);
if (!sb->dest.sa4.sin_family)
sb6 = to_v6_sa(&sb->gateway);
/*
* We shouldn't come here unless sa and sb both have AF_INET6 addresses
*/
assert(sa6->sin6_family == AF_INET6);
assert(sb6->sin6_family == AF_INET6);
/*
* Rule 1: Prefer same address.
* If SA = D, then prefer SA. Similarly, if SB = D, then prefer SB.
*/
if (!memcmp(&sa6->sin6_addr, &dst->sin6_addr, 16))
return SAS_PREFER_A;
if (!memcmp(&sb6->sin6_addr, &dst->sin6_addr, 16))
return SAS_PREFER_B;
/*
* Rule 2: Prefer appropriate scope.
* If Scope(SA) < Scope(SB): If Scope(SA) < Scope(D), then prefer SB
* and otherwise prefer SA.
*
* Similarly, if Scope(SB) < Scope(SA): If Scope(SB) < Scope(D), then
* prefer SA and otherwise prefer SB.
*/
scopea = lws_sort_dns_scope(to_sa46_sa(sa6));
scopeb = lws_sort_dns_scope(to_sa46_sa(sb6));
scoped = lws_sort_dns_scope(to_sa46_sa(dst));
if (scopea < scopeb)
return scopea < scoped ? SAS_PREFER_B : SAS_PREFER_A;
if (scopeb < scopea)
return scopeb < scoped ? SAS_PREFER_A : SAS_PREFER_B;
/*
* Rule 3: Avoid deprecated addresses.
* If one of the two source addresses is "preferred" and one of them
* is "deprecated" (in the RFC 4862 sense), then prefer the one that
* is "preferred".
*/
if (!(sa->ifa_flags & IFA_F_DEPRECATED) &&
(sb->ifa_flags & IFA_F_DEPRECATED))
return SAS_PREFER_A;
if ( (sa->ifa_flags & IFA_F_DEPRECATED) &&
!(sb->ifa_flags & IFA_F_DEPRECATED))
return SAS_PREFER_B;
/*
* Rule 4: Prefer home addresses.
* If SA is simultaneously a home address and care-of address and SB is
* not, then prefer SA. Similarly, if SB is simultaneously a home
* address and care-of address and SA is not, then prefer SB. If SA is
* just a home address and SB is just a care-of address, then prefer SA.
* Similarly, if SB is just a home address and SA is just a care-of
* address, then prefer SB.
*
* !!! not sure how to determine if care-of address
*/
if ( (sa->ifa_flags & IFA_F_HOMEADDRESS) &&
!(sb->ifa_flags & IFA_F_HOMEADDRESS))
return SAS_PREFER_A;
if (!(sa->ifa_flags & IFA_F_HOMEADDRESS) &&
(sb->ifa_flags & IFA_F_HOMEADDRESS))
return SAS_PREFER_B;
/*
* Rule 5: Prefer outgoing interface.
* If SA is assigned to the interface that will be used to send to D
* and SB is assigned to a different interface, then prefer SA.
* Similarly, if SB is assigned to the interface that will be used
* to send to D and SA is assigned to a different interface, then
* prefer SB.
*/
rd = _lws_route_est_outgoing(pt, (lws_sockaddr46 *)dst);
if (rd) {
if (rd->if_idx == sa->if_idx)
return SAS_PREFER_A;
if (rd->if_idx == sb->if_idx)
return SAS_PREFER_B;
}
/*
* Rule 6: Prefer matching label.
* If Label(SA) = Label(D) and Label(SB) <> Label(D), then prefer SA.
* Similarly, if Label(SB) = Label(D) and Label(SA) <> Label(D), then
* prefer SB.
*/
lws_sort_dns_classify(to_sa46_sa(sa6), &scorea);
lws_sort_dns_classify(to_sa46_sa(sb6), &scoreb);
lws_sort_dns_classify(to_sa46_sa(dst), &scoredst);
if (scorea.label == scoredst.label && scoreb.label != scoredst.label)
return SAS_PREFER_A;
if (scoreb.label == scoredst.label && scorea.label != scoredst.label)
return SAS_PREFER_B;
/*
* Rule 7: Prefer temporary addresses.
* If SA is a temporary address and SB is a public address, then
* prefer SA. Similarly, if SB is a temporary address and SA is a
* public address, then prefer SB.
*/
if ( (sa->ifa_flags & IFA_F_TEMPORARY) &&
!(sb->ifa_flags & IFA_F_TEMPORARY))
return SAS_PREFER_A;
if (!(sa->ifa_flags & IFA_F_TEMPORARY) &&
(sb->ifa_flags & IFA_F_TEMPORARY))
return SAS_PREFER_B;
/*
* Rule 8: Use longest matching prefix.
* If CommonPrefixLen(SA, D) > CommonPrefixLen(SB, D), then prefer SA.
* Similarly, if CommonPrefixLen(SB, D) > CommonPrefixLen(SA, D), then
* prefer SB.
*/
mla = lws_ipv6_prefix_match_len(sa6, dst);
mlb = lws_ipv6_prefix_match_len(sb6, dst);
if (mla > mlb)
return SAS_PREFER_A;
return SAS_SAME;
}
/*
* Given two possible source addresses and the destination address, we attempt
* to pick which one is "better".
*
* This implements RFC6724 Section 6.
*/
static int
lws_sort_dns_dcomp(const lws_dns_sort_t *da, const lws_dns_sort_t *db)
{
int scopea, scopeb, scope_srca, scope_srcb, cpla, cplb;
const uint8_t *da_ads = (const uint8_t *)&da->dest.sa6.sin6_addr,
*db_ads = (const uint8_t *)&db->dest.sa6.sin6_addr;
lws_dns_score_t score_srca, score_srcb;
/*
* Rule 1: Avoid unusable destinations
*
* We already strip destinations with no usable source
*/
/*
* Rule 2: Prefer matching scope
*
* If Scope(DA) = Scope(Source(DA)) and Scope(DB) <> Scope(Source(DB)),
* then prefer DA. Similarly, if Scope(DA) <> Scope(Source(DA)) and
* Scope(DB) = Scope(Source(DB)), then prefer DB.
*/
scopea = lws_ipv6_unicast_scope(to_v6_sa(&da->dest));
scopeb = lws_ipv6_unicast_scope(to_v6_sa(&db->dest));
scope_srca = lws_ipv6_unicast_scope(to_v6_sa(&da->source));
scope_srcb = lws_ipv6_unicast_scope(to_v6_sa(&db->source));
if (scopea == scope_srca && scopeb != scope_srcb)
return SAS_PREFER_A;
if (scopea != scope_srca && scopeb == scope_srcb)
return SAS_PREFER_B;
#if defined(IFA_F_DEPRECATED)
/*
* Rule 3: Avoid deprecated addresses.
*
* If Source(DA) is deprecated and Source(DB) is not, then prefer DB.
* Similarly, if Source(DA) is not deprecated and Source(DB) is
* deprecated, then prefer DA.
*/
if (!(da->ifa_flags & IFA_F_DEPRECATED) &&
(db->ifa_flags & IFA_F_DEPRECATED))
return SAS_PREFER_A;
if ( (da->ifa_flags & IFA_F_DEPRECATED) &&
!(db->ifa_flags & IFA_F_DEPRECATED))
return SAS_PREFER_B;
#endif
/*
* Rule 4: Prefer home addresses.
*
* If Source(DA) is simultaneously a home address and care-of address
* and Source(DB) is not, then prefer DA. Similarly, if Source(DB) is
* simultaneously a home address and care-of address and Source(DA) is
* not, then prefer DB.
*
* If Source(DA) is just a home address and Source(DB) is just a care-of
* address, then prefer DA. Similarly, if Source(DA) is just a care-of
* address and Source(DB) is just a home address, then prefer DB.
*
* !!! not sure how to determine if care-of address
*/
if ( (da->ifa_flags & IFA_F_HOMEADDRESS) &&
!(db->ifa_flags & IFA_F_HOMEADDRESS))
return SAS_PREFER_A;
if (!(da->ifa_flags & IFA_F_HOMEADDRESS) &&
(db->ifa_flags & IFA_F_HOMEADDRESS))
return SAS_PREFER_B;
/*
* Rule 5: Prefer matching label.
*
* If Label(Source(DA)) = Label(DA) and Label(Source(DB)) <> Label(DB),
* then prefer DA. Similarly, if Label(Source(DA)) <> Label(DA) and
* Label(Source(DB)) = Label(DB), then prefer DB
*/
if (!da->source)
return SAS_PREFER_B;
if (!db->source)
return SAS_PREFER_A;
lws_sort_dns_classify(&da->source->dest, &score_srca);
lws_sort_dns_classify(&db->source->dest, &score_srcb);
if (score_srca.label == da->score.label &&
score_srcb.label != db->score.label)
return SAS_PREFER_A;
if (score_srca.label != da->score.label &&
score_srcb.label == db->score.label)
return SAS_PREFER_B;
/*
* Rule 6: Prefer higher precedence.
*
* If Precedence(DA) > Precedence(DB), then prefer DA. Similarly, if
* Precedence(DA) < Precedence(DB), then prefer DB.
*/
if (da->score.precedence > db->score.precedence)
return SAS_PREFER_A;
if (da->score.precedence < db->score.precedence)
return SAS_PREFER_B;
/*
* Rule 7: Prefer native transport.
* If DA is reached via an encapsulating transition mechanism (e.g.,
* IPv6 in IPv4) and DB is not, then prefer DB. Similarly, if DB is
* reached via encapsulation and DA is not, then prefer DA.
*/
if (!memcmp(&ma[16], da_ads, 12) && memcmp(&ma[16], db_ads, 12))
return SAS_PREFER_B;
if (memcmp(&ma[16], da_ads, 12) && !memcmp(&ma[16], db_ads, 12))
return SAS_PREFER_A;
/*
* Rule 8: Prefer smaller scope.
* If Scope(DA) < Scope(DB), then prefer DA. Similarly, if Scope(DA) >
* Scope(DB), then prefer DB.
*/
if (scopea < scopeb)
return SAS_PREFER_A;
if (scopea > scopeb)
return SAS_PREFER_B;
/*
* Rule 9: Use longest matching prefix.
* When DA and DB belong to the same address family (both are IPv6 or
* both are IPv4): If CommonPrefixLen(Source(DA), DA) >
* CommonPrefixLen(Source(DB), DB), then prefer DA. Similarly, if
* CommonPrefixLen(Source(DA), DA) < CommonPrefixLen(Source(DB), DB),
* then prefer DB.
*/
cpla = lws_ipv6_prefix_match_len(&da->source->dest.sa6, &da->dest.sa6);
cplb = lws_ipv6_prefix_match_len(&db->source->dest.sa6, &db->dest.sa6);
if (cpla > cplb)
return SAS_PREFER_A;
if (cpla < cplb)
return SAS_PREFER_B;
/*
* Rule 10: Otherwise, leave the order unchanged.
*/
return SAS_SAME;
}
static int
lws_sort_dns_compare(const lws_dll2_t *a, const lws_dll2_t *b)
{
const lws_dns_sort_t *sa = lws_container_of(a, lws_dns_sort_t, list),
*sb = lws_container_of(b, lws_dns_sort_t, list);
return lws_sort_dns_dcomp(sa, sb);
}
#endif /* ipv6 + netlink */
#if defined(_DEBUG)
static void
lws_sort_dns_dump(struct lws *wsi)
{
int n = 1;
(void)n; /* nologs */
if (!lws_dll2_get_head(&wsi->dns_sorted_list))
lwsl_wsi_notice(wsi, "empty");
lws_start_foreach_dll(struct lws_dll2 *, d,
lws_dll2_get_head(&wsi->dns_sorted_list)) {
lws_dns_sort_t *s = lws_container_of(d, lws_dns_sort_t, list);
char dest[48], gw[48];
lws_sa46_write_numeric_address(&s->dest, dest, sizeof(dest));
lws_sa46_write_numeric_address(&s->gateway, gw, sizeof(gw));
lwsl_wsi_info(wsi, "%d: (%d)%s, gw (%d)%s, idi: %d, "
"lbl: %d, prec: %d", n++,
s->dest.sa4.sin_family, dest,
s->gateway.sa4.sin_family, gw,
s->if_idx, s->score.label, s->score.precedence);
} lws_end_foreach_dll(d);
}
#endif
int
lws_sort_dns(struct lws *wsi, const struct addrinfo *result)
{
#if defined(LWS_WITH_NETLINK)
struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
#endif
const struct addrinfo *ai = result;
lwsl_wsi_info(wsi, "sort_dns: %p", result);
/*
* We're going to take the dns results and produce our own linked-list
* of them, if we can sorted into descending preferability order, and
* possibly filtered.
*
* First let's just convert the addrinfo list into our expanded
* lws_dns_sort_t list, we can discard the addrinfo list then
*/
while (ai) {
#if defined(LWS_WITH_NETLINK) || \
(defined(LWS_WITH_NETLINK) && defined(LWS_WITH_IPV6))
lws_route_t
#if defined(LWS_WITH_NETLINK)
*estr = NULL
#endif
#if defined(LWS_WITH_NETLINK) && defined(LWS_WITH_IPV6)
, *bestsrc = NULL
#endif
;
#endif
lws_dns_sort_t *ds;
char afip[48];
/*
* Only transfer address families we can cope with
*/
if ((int)ai->ai_addrlen > (int)sizeof(lws_sockaddr46) ||
(ai->ai_family != AF_INET && ai->ai_family != AF_INET6))
goto next;
ds = lws_zalloc(sizeof(*ds), __func__);
if (!ds)
return 1;
memcpy(&ds->dest, ai->ai_addr, (size_t)ai->ai_addrlen);
ds->dest.sa4.sin_family = (sa_family_t)ai->ai_family;
lws_sa46_write_numeric_address(&ds->dest, afip, sizeof(afip));
lwsl_wsi_info(wsi, "unsorted entry (af %d) %s",
ds->dest.sa4.sin_family, afip);
#if defined(LWS_WITH_NETLINK)
/*
* Let's assess this DNS result in terms of route
* selection, eg, if no usable net route or gateway for it,
* we don't have a way to use it if we listed it
*/
if (pt->context->routing_table.count) {
estr = _lws_route_est_outgoing(pt, &ds->dest);
if (!estr) {
lws_free(ds);
lwsl_wsi_notice(wsi, "%s has no route out\n",
afip);
/*
* There's no outbound route for this, it's
* unusable, so don't add it to the list
*/
goto next;
}
ds->if_idx = estr->if_idx;
ds->uidx = estr->uidx;
/*
* ...evidently, there's a way for it to go out...
*/
}
#endif
#if defined(LWS_WITH_NETLINK) && defined(LWS_WITH_IPV6)
/*
* These sorting rules only apply to ipv6. If we have ipv4
* dest and estimate we will use an ipv4 source address to
* route it, then skip this.
*
* However if we have ipv4 dest and estimate we will use an
* ipv6 source address to route it, because of ipv6-only
* egress, then promote it to ipv6 and sort it
*/
if (ds->dest.sa4.sin_family == AF_INET) {
if (!estr ||
estr->dest.sa4.sin_family == AF_INET ||
estr->gateway.sa4.sin_family == AF_INET)
/*
* No estimated route, or v4 estimated route,
* just add it to sorted list
*/
goto just_add;
/*
* v4 dest on estimated v6 source ads route, because
* eg, there's no active v4 source ads just ipv6...
* promote v4 -> v6 address using ::ffff:xx:yy
*/
lwsl_wsi_info(wsi, "promoting v4->v6");
lws_sa46_4to6(&ds->dest,
(uint8_t *)&ds->dest.sa4.sin_addr, 0);
}
/* first, classify this destination ads */
lws_sort_dns_classify(&ds->dest, &ds->score);
/*
* RFC6724 Section 5: Source Address Selection
*
* Go through the source options choosing the best for this
* destination... this can only operate on ipv6 destination
* address
*/
lws_start_foreach_dll(struct lws_dll2 *, d,
lws_dll2_get_head(&pt->context->routing_table)) {
lws_route_t *r = lws_container_of(d, lws_route_t, list);
/* gateway routes are skipped here */
if (ds->dest.sa6.sin6_family == AF_INET6 &&
r->dest.sa4.sin_family == AF_INET6 && (!bestsrc ||
lws_sort_dns_scomp(pt, bestsrc, r, &ds->dest.sa6) ==
SAS_PREFER_B))
bestsrc = r;
} lws_end_foreach_dll(d);
/* bestsrc is the best source route, or NULL if none */
if (!bestsrc && pt->context->routing_table.count) {
/* drop it, no usable source route */
lws_free(ds);
goto next;
}
just_add:
if (!bestsrc) {
lws_dll2_add_tail(&ds->list, &wsi->dns_sorted_list);
goto next;
}
ds->source = bestsrc;
/*
* RFC6724 Section 6: Destination Address Selection
*
* Insert the destination into the list at a position reflecting
* its preferability, so the head entry is the most preferred
*/
lws_dll2_add_sorted(&ds->list, &wsi->dns_sorted_list,
lws_sort_dns_compare);
#else
/*
* We don't have the routing table + source address details in
* order to sort the DNS results... simply make entries in the
* order of the addrinfo results
*/
lws_dll2_add_tail(&ds->list, &wsi->dns_sorted_list);
#endif
next:
ai = ai->ai_next;
}
//lwsl_notice("%s: sorted table: %d\n", __func__,
// wsi->dns_sorted_list.count);
#if defined(_DEBUG)
lws_sort_dns_dump(wsi);
#endif
return !wsi->dns_sorted_list.count;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,869 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* 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 "private-lib-core.h"
/* max individual proxied header payload size */
#define MAXHDRVAL 1024
#if defined(LWS_WITH_HTTP_PROXY)
static int
proxy_header(struct lws *wsi, struct lws *par, unsigned char *temp,
int temp_len, int index, unsigned char **p, unsigned char *end)
{
int n = lws_hdr_total_length(par, (enum lws_token_indexes)index);
if (n < 1) {
lwsl_wsi_debug(wsi, "no index %d:", index);
return 0;
}
if (lws_hdr_copy(par, (char *)temp, temp_len, (enum lws_token_indexes)index) < 0) {
lwsl_wsi_notice(wsi, "unable to copy par hdr idx %d (len %d)",
index, n);
return -1;
}
lwsl_wsi_debug(wsi, "index %d: %s", index, (char *)temp);
if (lws_add_http_header_by_token(wsi, (enum lws_token_indexes)index, temp, n, p, end)) {
lwsl_wsi_notice(wsi, "unable to append par hdr idx %d (len %d)",
index, n);
return -1;
}
return 0;
}
static int
stream_close(struct lws *wsi)
{
char buf[LWS_PRE + 6], *out = buf + LWS_PRE;
if (wsi->http.did_stream_close)
return 0;
wsi->http.did_stream_close = 1;
if (wsi->mux_substream) {
if (lws_write(wsi, (unsigned char *)buf + LWS_PRE, 0,
LWS_WRITE_HTTP_FINAL) < 0)
goto bail;
return 0;
}
*out++ = '0';
*out++ = '\x0d';
*out++ = '\x0a';
*out++ = '\x0d';
*out++ = '\x0a';
if (lws_write(wsi, (unsigned char *)buf + LWS_PRE, 5,
LWS_WRITE_HTTP_FINAL) < 0)
goto bail;
return 0;
bail:
lwsl_wsi_info(wsi, "h2 fin wr failed");
return -1;
}
#endif
struct lws_proxy_pkt {
struct lws_dll2 pkt_list;
size_t len;
char binary;
char first;
char final;
/* data follows */
};
#if defined(LWS_WITH_HTTP_PROXY) && defined(LWS_ROLE_WS)
int
lws_callback_ws_proxy(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
struct lws_proxy_pkt *pkt;
struct lws_dll2 *dll;
switch (reason) {
/* h1 ws proxying... child / client / onward */
case LWS_CALLBACK_CLIENT_ESTABLISHED:
if (!wsi->h1_ws_proxied || !wsi->parent)
break;
if (lws_process_ws_upgrade2(wsi->parent))
return -1;
#if defined(LWS_WITH_HTTP2)
if (wsi->parent->mux_substream)
lwsl_wsi_info(wsi, "proxied h2 -> h1 ws established");
#endif
break;
case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
return 1;
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
case LWS_CALLBACK_CLIENT_CLOSED:
lwsl_wsi_info(wsi, "client closed: parent %s",
lws_wsi_tag(wsi->parent));
if (wsi->parent)
lws_set_timeout(wsi->parent, 1, LWS_TO_KILL_ASYNC);
break;
case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
{
unsigned char **p = (unsigned char **)in, *end = (*p) + len,
tmp[MAXHDRVAL];
proxy_header(wsi, wsi->parent, tmp, sizeof(tmp),
WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, p, end);
proxy_header(wsi, wsi->parent, tmp, sizeof(tmp),
WSI_TOKEN_HTTP_COOKIE, p, end);
proxy_header(wsi, wsi->parent, tmp, sizeof(tmp),
WSI_TOKEN_HTTP_SET_COOKIE, p, end);
break;
}
case LWS_CALLBACK_CLIENT_RECEIVE:
wsi->parent->ws->proxy_buffered += len;
if (wsi->parent->ws->proxy_buffered > 10 * 1024 * 1024) {
lwsl_wsi_err(wsi, "proxied ws connection "
"excessive buffering: dropping");
return -1;
}
pkt = lws_zalloc(sizeof(*pkt) + LWS_PRE + len, __func__);
if (!pkt)
return -1;
pkt->len = len;
pkt->first = (char)lws_is_first_fragment(wsi);
pkt->final = (char)lws_is_final_fragment(wsi);
pkt->binary = (char)lws_frame_is_binary(wsi);
memcpy(((uint8_t *)&pkt[1]) + LWS_PRE, in, len);
lws_dll2_add_tail(&pkt->pkt_list, &wsi->parent->ws->proxy_owner);
lws_callback_on_writable(wsi->parent);
break;
case LWS_CALLBACK_CLIENT_WRITEABLE:
dll = lws_dll2_get_head(&wsi->ws->proxy_owner);
if (!dll)
break;
pkt = (struct lws_proxy_pkt *)dll;
if (lws_write(wsi, ((unsigned char *)&pkt[1]) +
LWS_PRE, pkt->len, (enum lws_write_protocol)lws_write_ws_flags(
pkt->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT,
pkt->first, pkt->final)) < 0)
return -1;
lws_dll2_remove(dll);
lws_free(pkt);
if (lws_dll2_get_head(&wsi->ws->proxy_owner))
lws_callback_on_writable(wsi);
break;
/* h1 ws proxying... parent / server / incoming */
case LWS_CALLBACK_CONFIRM_EXTENSION_OKAY:
return 1;
case LWS_CALLBACK_CLOSED:
lwsl_wsi_info(wsi, "closed");
return -1;
case LWS_CALLBACK_RECEIVE:
pkt = lws_zalloc(sizeof(*pkt) + LWS_PRE + len, __func__);
if (!pkt)
return -1;
pkt->len = len;
pkt->first = (char)lws_is_first_fragment(wsi);
pkt->final = (char)lws_is_final_fragment(wsi);
pkt->binary = (char)lws_frame_is_binary(wsi);
memcpy(((uint8_t *)&pkt[1]) + LWS_PRE, in, len);
lws_dll2_add_tail(&pkt->pkt_list, &wsi->child_list->ws->proxy_owner);
lws_callback_on_writable(wsi->child_list);
break;
case LWS_CALLBACK_SERVER_WRITEABLE:
dll = lws_dll2_get_head(&wsi->ws->proxy_owner);
if (!dll)
break;
pkt = (struct lws_proxy_pkt *)dll;
if (lws_write(wsi, ((unsigned char *)&pkt[1]) +
LWS_PRE, pkt->len, (enum lws_write_protocol)lws_write_ws_flags(
pkt->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT,
pkt->first, pkt->final)) < 0)
return -1;
wsi->ws->proxy_buffered -= pkt->len;
lws_dll2_remove(dll);
lws_free(pkt);
if (lws_dll2_get_head(&wsi->ws->proxy_owner))
lws_callback_on_writable(wsi);
break;
default:
return 0;
}
return 0;
}
const struct lws_protocols lws_ws_proxy = {
"lws-ws-proxy",
lws_callback_ws_proxy,
0,
8192,
8192, NULL, 0
};
#endif
int
lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
struct lws_ssl_info *si;
#ifdef LWS_WITH_CGI
struct lws_cgi_args *args;
#endif
#if defined(LWS_WITH_CGI) || defined(LWS_WITH_HTTP_PROXY)
char buf[LWS_PRE + 32 + 8192];
int n;
#endif
#if defined(LWS_WITH_HTTP_PROXY)
unsigned char **p, *end;
struct lws *parent;
#endif
switch (reason) {
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
case LWS_CALLBACK_HTTP:
#if defined(LWS_WITH_SERVER)
if (lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL))
return -1;
if (lws_http_transaction_completed(wsi))
#endif
return -1;
break;
#if defined(LWS_WITH_SERVER)
case LWS_CALLBACK_HTTP_BODY_COMPLETION:
#if defined(LWS_WITH_HTTP_PROXY)
if (wsi->child_list) {
lwsl_wsi_info(wsi, "HTTP_BODY_COMPLETION: %d",
(int)len);
lws_callback_on_writable(wsi->child_list);
break;
}
#endif
if (lws_return_http_status(wsi, 200, NULL))
return -1;
break;
/* fallthru */
case LWS_CALLBACK_HTTP_FILE_COMPLETION:
if (lws_http_transaction_completed(wsi))
return -1;
break;
#endif
#if defined(LWS_WITH_HTTP_PROXY)
case LWS_CALLBACK_HTTP_BODY:
if (wsi->child_list) {
lwsl_wsi_info(wsi, "HTTP_BODY: stashing %d", (int)len);
if (lws_buflist_append_segment(
&wsi->http.buflist_post_body, in, len) < 0)
return -1;
lws_client_http_body_pending(wsi->child_list, 1);
lws_callback_on_writable(wsi->child_list);
}
break;
#endif
case LWS_CALLBACK_HTTP_WRITEABLE:
// lwsl_err("%s: LWS_CALLBACK_HTTP_WRITEABLE\n", __func__);
#ifdef LWS_WITH_CGI
if (wsi->reason_bf & (LWS_CB_REASON_AUX_BF__CGI_HEADERS |
LWS_CB_REASON_AUX_BF__CGI)) {
n = lws_cgi_write_split_stdout_headers(wsi);
if (n < 0) {
lwsl_wsi_debug(wsi, "AUX_BF__CGI forcing close");
return -1;
}
if (!n && wsi->http.cgi && wsi->http.cgi->lsp &&
wsi->http.cgi->lsp->stdwsi[LWS_STDOUT])
lws_rx_flow_control(
wsi->http.cgi->lsp->stdwsi[LWS_STDOUT], 1);
if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_HEADERS)
wsi->reason_bf &=
(char)~LWS_CB_REASON_AUX_BF__CGI_HEADERS;
else
wsi->reason_bf &= (char)~LWS_CB_REASON_AUX_BF__CGI;
if (wsi->http.cgi && wsi->http.cgi->cgi_transaction_over) {
lwsl_wsi_info(wsi, "txn over");
return -1;
}
break;
}
if ((wsi->http.cgi && wsi->http.cgi->cgi_transaction_over) ||
(wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_CHUNK_END)) {
if (!wsi->mux_substream) {
memcpy(buf + LWS_PRE, "0\x0d\x0a\x0d\x0a", 5);
lwsl_wsi_debug(wsi, "wr chunk term and exiting");
lws_write(wsi, (unsigned char *)buf +
LWS_PRE, 5, LWS_WRITE_HTTP);
} else
lws_write(wsi, (unsigned char *)buf +
LWS_PRE, 0,
LWS_WRITE_HTTP_FINAL);
/* always close after sending it */
if (lws_http_transaction_completed(wsi))
return -1;
return 0;
}
#endif
#if defined(LWS_WITH_HTTP_PROXY)
if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY_HEADERS) {
wsi->reason_bf &=
(char)~LWS_CB_REASON_AUX_BF__PROXY_HEADERS;
n = LWS_WRITE_HTTP_HEADERS;
if (!wsi->http.prh_content_length)
n |= LWS_WRITE_H2_STREAM_END;
lwsl_wsi_debug(wsi, "issuing proxy headers: clen %d",
(int)wsi->http.prh_content_length);
n = lws_write(wsi, wsi->http.pending_return_headers +
LWS_PRE,
wsi->http.pending_return_headers_len,
(enum lws_write_protocol)n);
lws_free_set_NULL(wsi->http.pending_return_headers);
if (n < 0) {
lwsl_wsi_err(wsi, "EST_CLIENT_HTTP: wr failed");
return -1;
}
lws_callback_on_writable(wsi);
break;
}
if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY) {
char *px = buf + LWS_PRE;
int lenx = sizeof(buf) - LWS_PRE - 32;
/*
* our sink is writeable and our source has something
* to read. So read a lump of source material of
* suitable size to send or what's available, whichever
* is the smaller.
*/
wsi->reason_bf &= (char)~LWS_CB_REASON_AUX_BF__PROXY;
if (!lws_get_child(wsi))
break;
/* this causes LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ */
if (lws_http_client_read(lws_get_child(wsi), &px,
&lenx) < 0) {
lwsl_wsi_info(wsi, "LWS_CB_REASON_AUX_BF__PROXY: "
"client closed");
stream_close(wsi);
return -1;
}
break;
}
if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY_TRANS_END) {
lwsl_wsi_info(wsi, "PROXY_TRANS_END");
wsi->reason_bf &= (char)~LWS_CB_REASON_AUX_BF__PROXY_TRANS_END;
if (stream_close(wsi))
return -1;
if (lws_http_transaction_completed(wsi))
return -1;
}
#endif
break;
#if defined(LWS_WITH_HTTP_PROXY)
case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
assert(lws_get_parent(wsi));
if (!lws_get_parent(wsi))
break;
lws_get_parent(wsi)->reason_bf |= LWS_CB_REASON_AUX_BF__PROXY;
lws_callback_on_writable(lws_get_parent(wsi));
break;
case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: {
char *out = buf + LWS_PRE;
assert(lws_get_parent(wsi));
if (wsi->http.proxy_parent_chunked) {
if (len > sizeof(buf) - LWS_PRE - 16) {
lwsl_wsi_err(wsi, "oversize buf %d %d", (int)len,
(int)sizeof(buf) - LWS_PRE - 16);
return -1;
}
/*
* this only needs dealing with on http/1.1 to allow
* pipelining
*/
n = lws_snprintf(out, 14, "%X\x0d\x0a", (int)len);
out += n;
memcpy(out, in, len);
out += len;
*out++ = '\x0d';
*out++ = '\x0a';
n = lws_write(lws_get_parent(wsi),
(unsigned char *)buf + LWS_PRE,
(size_t)(unsigned int)(len + (unsigned int)n + 2), LWS_WRITE_HTTP);
} else
n = lws_write(lws_get_parent(wsi), (unsigned char *)in,
len, LWS_WRITE_HTTP);
if (n < 0)
return -1;
break; }
/* h1 http proxying... */
case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: {
unsigned char *start, *p, *end;
/*
* We want to proxy these headers, but we are being called
* at the point the onward client was established, which is
* unrelated to the state or writability of our proxy
* connection.
*
* Therefore produce the headers using the onward client ah
* while we have it, and stick them on the output buflist to be
* written on the proxy connection as soon as convenient.
*/
parent = lws_get_parent(wsi);
if (!parent)
return 0;
start = p = (unsigned char *)buf + LWS_PRE;
end = p + sizeof(buf) - LWS_PRE - MAXHDRVAL;
if (lws_add_http_header_status(lws_get_parent(wsi),
lws_http_client_http_response(wsi), &p, end))
return 1;
/*
* copy these headers from the client connection to the parent
*/
proxy_header(parent, wsi, end, MAXHDRVAL,
WSI_TOKEN_HTTP_CONTENT_LENGTH, &p, end);
proxy_header(parent, wsi, end, MAXHDRVAL,
WSI_TOKEN_HTTP_CONTENT_TYPE, &p, end);
proxy_header(parent, wsi, end, MAXHDRVAL,
WSI_TOKEN_HTTP_ETAG, &p, end);
proxy_header(parent, wsi, end, MAXHDRVAL,
WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, &p, end);
proxy_header(parent, wsi, end, MAXHDRVAL,
WSI_TOKEN_HTTP_CONTENT_ENCODING, &p, end);
proxy_header(parent, wsi, end, MAXHDRVAL,
WSI_TOKEN_HTTP_CACHE_CONTROL, &p, end);
proxy_header(parent, wsi, end, MAXHDRVAL,
WSI_TOKEN_HTTP_SET_COOKIE, &p, end);
proxy_header(parent, wsi, end, MAXHDRVAL,
WSI_TOKEN_HTTP_LOCATION, &p, end);
if (!parent->mux_substream)
if (lws_add_http_header_by_token(parent,
WSI_TOKEN_CONNECTION, (unsigned char *)"close",
5, &p, end))
return -1;
/*
* We proxy using h1 only atm, and strip any chunking so it
* can go back out on h2 just fine.
*
* However if we are actually going out on h1, we need to add
* our own chunking since we still don't know the size.
*/
if (!parent->mux_substream &&
!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
lwsl_wsi_debug(wsi, "downstream parent chunked");
if (lws_add_http_header_by_token(parent,
WSI_TOKEN_HTTP_TRANSFER_ENCODING,
(unsigned char *)"chunked", 7, &p, end))
return -1;
wsi->http.proxy_parent_chunked = 1;
}
if (lws_finalize_http_header(parent, &p, end))
return 1;
parent->http.prh_content_length = (size_t)-1;
if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH))
parent->http.prh_content_length = (size_t)atoll(
lws_hdr_simple_ptr(wsi,
WSI_TOKEN_HTTP_CONTENT_LENGTH));
parent->http.pending_return_headers_len = lws_ptr_diff_size_t(p, start);
parent->http.pending_return_headers =
lws_malloc(parent->http.pending_return_headers_len +
LWS_PRE, "return proxy headers");
if (!parent->http.pending_return_headers)
return -1;
memcpy(parent->http.pending_return_headers + LWS_PRE, start,
parent->http.pending_return_headers_len);
parent->reason_bf |= LWS_CB_REASON_AUX_BF__PROXY_HEADERS;
lwsl_wsi_debug(wsi, "ESTABLISHED_CLIENT_HTTP: "
"prepared %d headers (len %d)",
lws_http_client_http_response(wsi),
(int)parent->http.prh_content_length);
/*
* so at this point, the onward client connection can bear
* traffic. We might be doing a POST and have pending cached
* inbound stuff to send, it can go now.
*/
lws_callback_on_writable(parent);
break; }
case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
lwsl_wsi_info(wsi, "COMPLETED_CLIENT_HTTP: (parent %s)",
lws_wsi_tag(lws_get_parent(wsi)));
if (!lws_get_parent(wsi))
break;
lws_get_parent(wsi)->reason_bf |=
LWS_CB_REASON_AUX_BF__PROXY_TRANS_END;
lws_callback_on_writable(lws_get_parent(wsi));
break;
case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
if (!lws_get_parent(wsi))
break;
// lwsl_err("%s: LWS_CALLBACK_CLOSED_CLIENT_HTTP\n", __func__);
lws_set_timeout(lws_get_parent(wsi),
(enum pending_timeout)LWS_TO_KILL_ASYNC,
(int)PENDING_TIMEOUT_KILLED_BY_PROXY_CLIENT_CLOSE);
break;
case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
parent = lws_get_parent(wsi);
if (!parent)
break;
p = (unsigned char **)in;
end = (*p) + len;
/*
* copy these headers from the parent request to the client
* connection's request
*/
proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
WSI_TOKEN_HTTP_ETAG, p, end);
proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
WSI_TOKEN_HTTP_IF_MODIFIED_SINCE, p, end);
proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, p, end);
proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
WSI_TOKEN_HTTP_ACCEPT_ENCODING, p, end);
proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
WSI_TOKEN_HTTP_CACHE_CONTROL, p, end);
proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
WSI_TOKEN_HTTP_COOKIE, p, end);
buf[0] = '\0';
lws_get_peer_simple(parent, buf, sizeof(buf));
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_X_FORWARDED_FOR,
(unsigned char *)buf, (int)strlen(buf), p, end))
return -1;
break;
#endif
#ifdef LWS_WITH_CGI
/* CGI IO events (POLLIN/OUT) appear here, our default policy is:
*
* - POST data goes on subprocess stdin
* - subprocess stdout goes on http via writeable callback
* - subprocess stderr goes to the logs
*/
case LWS_CALLBACK_CGI:
args = (struct lws_cgi_args *)in;
switch (args->ch) { /* which of stdin/out/err ? */
case LWS_STDIN:
/* TBD stdin rx flow control */
break;
case LWS_STDOUT:
if (args->stdwsi[LWS_STDOUT])
/* quench POLLIN on STDOUT until MASTER got writeable */
lws_rx_flow_control(args->stdwsi[LWS_STDOUT], 0);
wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI;
/* when writing to MASTER would not block */
lws_callback_on_writable(wsi);
break;
case LWS_STDERR:
n = lws_get_socket_fd(args->stdwsi[LWS_STDERR]);
if (n < 0)
break;
n = (int)read(n, buf, sizeof(buf) - 2);
if (n > 0) {
if (buf[n - 1] != '\n')
buf[n++] = '\n';
buf[n] = '\0';
lwsl_wsi_notice(wsi, "CGI-stderr: %s", buf);
}
break;
}
break;
case LWS_CALLBACK_CGI_TERMINATED:
if (wsi->http.cgi) {
lwsl_wsi_debug(wsi, "CGI_TERMINATED: %d %" PRIu64,
wsi->http.cgi->explicitly_chunked,
(uint64_t)wsi->http.cgi->content_length);
if (!(wsi->http.cgi->explicitly_chunked && wsi->mux_substream) &&
!wsi->http.cgi->content_length) {
/* send terminating chunk */
lwsl_wsi_debug(wsi, "LWS_CALLBACK_CGI_TERMINATED: ending");
wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_CHUNK_END;
lws_callback_on_writable(wsi);
lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, 3);
break;
}
if (wsi->mux_substream && !wsi->cgi_stdout_zero_length)
lws_write(wsi, (unsigned char *)buf + LWS_PRE, 0,
LWS_WRITE_HTTP_FINAL);
}
#if defined(LWS_WITH_SERVER)
if (lws_http_transaction_completed(wsi))
return -1;
#endif
return 0;
case LWS_CALLBACK_CGI_STDIN_DATA: /* POST body for stdin */
args = (struct lws_cgi_args *)in;
args->data[args->len] = '\0';
if (!args->stdwsi[LWS_STDIN])
return -1;
n = lws_get_socket_fd(args->stdwsi[LWS_STDIN]);
if (n < 0)
return -1;
#if defined(LWS_WITH_ZLIB)
if (wsi->http.cgi->gzip_inflate) {
/* gzip handling */
if (!wsi->http.cgi->gzip_init) {
lwsl_wsi_info(wsi, "inflating gzip");
memset(&wsi->http.cgi->inflate, 0,
sizeof(wsi->http.cgi->inflate));
if (inflateInit2(&wsi->http.cgi->inflate,
16 + 15) != Z_OK) {
lwsl_wsi_err(wsi, "iniflateInit fail");
return -1;
}
wsi->http.cgi->gzip_init = 1;
}
wsi->http.cgi->inflate.next_in = args->data;
wsi->http.cgi->inflate.avail_in = (unsigned int)args->len;
do {
wsi->http.cgi->inflate.next_out =
wsi->http.cgi->inflate_buf;
wsi->http.cgi->inflate.avail_out =
sizeof(wsi->http.cgi->inflate_buf);
n = inflate(&wsi->http.cgi->inflate,
Z_SYNC_FLUSH);
switch (n) {
case Z_NEED_DICT:
case Z_STREAM_ERROR:
case Z_DATA_ERROR:
case Z_MEM_ERROR:
inflateEnd(&wsi->http.cgi->inflate);
wsi->http.cgi->gzip_init = 0;
lwsl_wsi_err(wsi, "zlib err inflate %d", n);
return -1;
}
if (wsi->http.cgi->inflate.avail_out !=
sizeof(wsi->http.cgi->inflate_buf)) {
int written;
written = (int)write(args->stdwsi[LWS_STDIN]->desc.filefd,
wsi->http.cgi->inflate_buf,
sizeof(wsi->http.cgi->inflate_buf) -
wsi->http.cgi->inflate.avail_out);
if (written != (int)(
sizeof(wsi->http.cgi->inflate_buf) -
wsi->http.cgi->inflate.avail_out)) {
lwsl_wsi_notice(wsi,
"CGI_STDIN_DATA: "
"sent %d only %d went",
n, args->len);
}
if (n == Z_STREAM_END) {
lwsl_wsi_err(wsi,
"gzip inflate end");
inflateEnd(&wsi->http.cgi->inflate);
wsi->http.cgi->gzip_init = 0;
break;
}
} else
break;
if (wsi->http.cgi->inflate.avail_out)
break;
} while (1);
return args->len;
}
#endif /* WITH_ZLIB */
n = (int)write(n, args->data, (unsigned int)args->len);
// lwsl_hexdump_notice(args->data, args->len);
if (n < args->len)
lwsl_wsi_notice(wsi, "CGI_STDIN_DATA: "
"sent %d only %d went", n, args->len);
lwsl_wsi_info(wsi, "proxied %d bytes", n);
if (wsi->http.cgi->post_in_expected && args->stdwsi[LWS_STDIN] &&
args->stdwsi[LWS_STDIN]->desc.filefd > 0) {
wsi->http.cgi->post_in_expected -= (unsigned int)n;
if (!wsi->http.cgi->post_in_expected) {
struct lws *siwsi = args->stdwsi[LWS_STDIN];
/*
* The situation here is that we finished
* proxying the incoming body from the net to
* the STDIN stdwsi... and we want to close it
* so it can understand we are done (necessary
* if no content-length)...
*/
lwsl_wsi_info(siwsi, "expected POST in end: "
"closing stdin fd %d",
siwsi->desc.sockfd);
/*
* We don't want the child / parent relationship
* to be handled in close, since we want the
* rest of the cgi and children to stay up
*/
lws_remove_child_from_any_parent(siwsi);
lws_wsi_close(siwsi, LWS_TO_KILL_ASYNC);
wsi->http.cgi->lsp->stdwsi[LWS_STDIN] = NULL;
lws_spawn_stdwsi_closed(wsi->http.cgi->lsp, siwsi);
}
}
return n;
#endif /* WITH_CGI */
#endif /* ROLE_ H1 / H2 */
case LWS_CALLBACK_SSL_INFO:
si = in;
(void)si;
lwsl_wsi_notice(wsi, "SSL_INFO: where: 0x%x, ret: 0x%x",
si->where, si->ret);
break;
#if LWS_MAX_SMP > 1
case LWS_CALLBACK_GET_THREAD_ID:
#ifdef __PTW32_H
/* If we use implementation of PThreads for Win that is
* distributed by VCPKG */
return (int)(lws_intptr_t)(pthread_self()).p;
#else
return (int)(lws_intptr_t)pthread_self();
#endif // __PTW32_H
#endif
default:
break;
}
return 0;
}

View File

@ -0,0 +1,647 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* 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 "private-lib-core.h"
#if defined(STANDALONE)
#undef lws_malloc
#define lws_malloc(a, b) malloc(a)
#undef lws_free
#define lws_free(a) free(a)
#undef lws_free_set_NULL
#define lws_free_set_NULL(a) { if (a) { free(a); a = NULL; }}
#endif
struct lws_dsh_search {
size_t required;
ssize_t natural_required;
int kind;
lws_dsh_obj_t *best;
lws_dsh_t *dsh;
lws_dsh_obj_t *tail_obj;
void *natural; /* coalesce address against last tail */
lws_dsh_t *already_checked;
lws_dsh_t *this_dsh;
char coalesce;
};
static int
_lws_dsh_alloc_tail(lws_dsh_t *dsh, int kind, const void *src1, size_t size1,
const void *src2, size_t size2, lws_dll2_t *replace);
static size_t
lws_dsh_align(size_t length)
{
size_t align = sizeof(int *);
if (length & (align - 1))
length += align - (length & (align - 1));
return length;
}
void
lws_dsh_empty(struct lws_dsh *dsh)
{
lws_dsh_obj_t *obj;
size_t oha_len;
int n;
if (!dsh)
return;
oha_len = sizeof(lws_dsh_obj_head_t) * (unsigned int)dsh->count_kinds;
/* clear down the obj heads array */
memset(dsh->oha, 0, oha_len);
for (n = 0; n < dsh->count_kinds; n++) {
dsh->oha[n].kind = n;
dsh->oha[n].total_size = 0;
}
/* initially the whole buffer is on the free kind (0) list */
obj = (lws_dsh_obj_t *)dsh->buf;
memset(obj, 0, sizeof(*obj));
obj->asize = dsh->buffer_size - sizeof(*obj);
lws_dll2_add_head(&obj->list, &dsh->oha[0].owner);
dsh->locally_free = obj->asize;
dsh->locally_in_use = 0;
}
lws_dsh_t *
lws_dsh_create(lws_dll2_owner_t *owner, size_t buf_len, int _count_kinds)
{
int count_kinds = _count_kinds & 0xff;
lws_dsh_t *dsh;
size_t oha_len;
oha_len = sizeof(lws_dsh_obj_head_t) * (unsigned int)(++count_kinds);
assert(buf_len);
assert(count_kinds > 1);
assert(buf_len > sizeof(lws_dsh_t) + oha_len);
buf_len += 64;
dsh = lws_malloc(sizeof(lws_dsh_t) + buf_len + oha_len, __func__);
if (!dsh)
return NULL;
/* set convenience pointers to the overallocated parts */
lws_dll2_clear(&dsh->list);
dsh->oha = (lws_dsh_obj_head_t *)&dsh[1];
dsh->buf = ((uint8_t *)dsh->oha) + oha_len;
dsh->count_kinds = count_kinds;
dsh->buffer_size = buf_len;
dsh->being_destroyed = 0;
dsh->splitat = 0;
dsh->flags = (unsigned int)_count_kinds & 0xff000000u;
lws_dsh_empty(dsh);
if (owner)
lws_dll2_add_head(&dsh->list, owner);
// lws_dsh_describe(dsh, "post-init");
return dsh;
}
/*
* We're flicking through the hole list... if we find a suitable hole starting
* right after the current tail, it means we can coalesce against the current
* tail, that overrides all other considerations
*/
static int
search_best_free(struct lws_dll2 *d, void *user)
{
struct lws_dsh_search *s = (struct lws_dsh_search *)user;
lws_dsh_obj_t *obj = lws_container_of(d, lws_dsh_obj_t, list);
// lwsl_debug("%s: obj %p, asize %zu (req %zu)\n", __func__, obj,
// obj->asize, s->required);
// if (s->tail_obj)
// lwsl_notice("%s: tail est %d, splitat %d\n", __func__,
// (int)(s->tail_obj->asize + (size_t)s->natural_required), (int)s->dsh->splitat);
if (s->dsh->flags & LWS_DSHFLAG_ENABLE_COALESCE) {
if (obj == s->natural && s->tail_obj &&
(int)obj->asize >= s->natural_required
&&
(!s->dsh->splitat ||
(size_t)(s->tail_obj->asize +
(size_t)s->natural_required) <= s->dsh->splitat)
) {
// lwsl_user("%s: found natural\n", __func__);
s->dsh = s->this_dsh;
s->best = obj;
s->coalesce = 1;
}
if (s->coalesce)
return 0;
}
if (obj->asize >= s->required &&
(!s->best || obj->asize < s->best->asize)) {
s->best = obj;
s->dsh = s->this_dsh;
}
return 0;
}
static int
buf_compare(const lws_dll2_t *d, const lws_dll2_t *i)
{
return (int)lws_ptr_diff(d, i);
}
void
lws_dsh_destroy(lws_dsh_t **pdsh)
{
lws_dsh_t *dsh = *pdsh;
if (!dsh)
return;
dsh->being_destroyed = 1;
lws_dll2_remove(&dsh->list);
lws_dsh_empty(dsh);
/* everything else is in one heap allocation */
lws_free_set_NULL(*pdsh);
}
size_t
lws_dsh_get_size(struct lws_dsh *dsh, int kind)
{
kind++;
assert(kind < dsh->count_kinds);
return dsh->oha[kind].total_size;
}
static int
_lws_dsh_alloc_tail(lws_dsh_t *dsh, int kind, const void *src1, size_t size1,
const void *src2, size_t size2, lws_dll2_t *replace)
{
size_t asize = sizeof(lws_dsh_obj_t) + lws_dsh_align(size1 + size2);
struct lws_dsh_search s;
assert(kind >= 0);
kind++;
assert(!dsh || kind < dsh->count_kinds);
/*
* Search our free list looking for the smallest guy who will fit
* what we want to allocate
*/
s.dsh = dsh;
s.required = asize;
s.kind = kind;
s.best = NULL;
s.already_checked = NULL;
s.this_dsh = dsh;
s.natural = NULL;
s.coalesce = 0;
s.natural_required = 0;
/* list is at the very start, so we can cast */
s.tail_obj = (lws_dsh_obj_t *)dsh->oha[kind].owner.tail;
if (s.tail_obj) {
assert(s.tail_obj->kind == kind);
/*
* there's a tail... precompute where a natural hole would
* have to start to be coalescable
*/
s.natural = (uint8_t *)s.tail_obj + s.tail_obj->asize;
/*
* ... and precompute the needed hole extent (including its
* obj part we would no longer need if we coalesced, and
* accounting for any unused / alignment part in the tail
*/
s.natural_required = (ssize_t)(lws_dsh_align(s.tail_obj->size + size1 + size2) -
s.tail_obj->asize + sizeof(lws_dsh_obj_t));
// lwsl_notice("%s: natural %p, tail len %d, nreq %d, splitat %d\n", __func__, s.natural,
// (int)s.tail_obj->size, (int)s.natural_required, (int)dsh->splitat);
}
if (dsh && !dsh->being_destroyed)
lws_dll2_foreach_safe(&dsh->oha[0].owner, &s, search_best_free);
if (!s.best) {
//lwsl_notice("%s: no buffer has space for %lu\n",
// __func__, (unsigned long)asize);
return 1;
}
if (s.coalesce) {
uint8_t *nf = (uint8_t *)&s.tail_obj[1] + s.tail_obj->size,
*e = (uint8_t *)s.best + s.best->asize, *ce;
lws_dsh_obj_t *rh;
size_t le;
// lwsl_notice("%s: coalescing\n", __func__);
/*
* logically remove the free list entry we're taking over the
* memory footprint of
*/
lws_dll2_remove(&s.best->list);
s.dsh->locally_free -= s.best->asize;
if (s.dsh->oha[kind].total_size < s.tail_obj->asize) {
lwsl_err("%s: total_size %d, asize %d, hdr size %d\n", __func__,
(int)s.dsh->oha[kind].total_size,
(int)s.tail_obj->asize, (int)sizeof(lws_dsh_obj_t));
assert(0);
}
s.dsh->oha[kind].total_size -= s.tail_obj->asize;
s.dsh->locally_in_use -= s.tail_obj->asize;
if (size1) {
memcpy(nf, src1, size1);
nf += size1;
}
if (size2) {
memcpy(nf, src2, size2);
nf += size2;
}
/*
* adjust the tail guy's sizes to account for the coalesced
* data and alignment for the end point
*/
s.tail_obj->size = s.tail_obj->size + size1 + size2;
s.tail_obj->asize = sizeof(lws_dsh_obj_t) +
lws_dsh_align(s.tail_obj->size);
ce = (uint8_t *)s.tail_obj + s.tail_obj->asize;
assert(ce <= e);
le = lws_ptr_diff_size_t(e, ce);
/*
* Now we have to decide what to do with any leftovers...
*/
if (le < 64)
/*
* just absorb it into the coalesced guy as spare, and
* no need for a replacement hole
*/
s.tail_obj->asize += le;
else {
rh = (lws_dsh_obj_t *)ce;
memset(rh, 0, sizeof(*rh));
rh->asize = le;
lws_dll2_add_sorted(&rh->list, &s.dsh->oha[0].owner,
buf_compare);
s.dsh->locally_free += rh->asize;
}
s.dsh->oha[kind].total_size += s.tail_obj->asize;
s.dsh->locally_in_use += s.tail_obj->asize;
return 0;
}
/* anything coming out of here must be aligned */
assert(!(((size_t)(intptr_t)s.best) & (sizeof(int *) - 1)));
if (s.best->asize < asize + (2 * sizeof(*s.best))) {
// lwsl_notice("%s: exact\n", __func__);
/*
* Exact fit, or close enough we can't / don't want to have to
* track the little bit of free area that would be left.
*
* Move the object from the free list to the oha of the
* desired kind
*/
lws_dll2_remove(&s.best->list);
s.best->dsh = s.dsh;
s.best->kind = kind;
s.best->size = size1 + size2;
memcpy(&s.best[1], src1, size1);
if (src2)
memcpy((uint8_t *)&s.best[1] + size1, src2, size2);
if (replace) {
s.best->list.prev = replace->prev;
s.best->list.next = replace->next;
s.best->list.owner = replace->owner;
if (replace->prev)
replace->prev->next = &s.best->list;
if (replace->next)
replace->next->prev = &s.best->list;
} else
if (dsh) {
assert(!(((unsigned long)(intptr_t)(s.best)) &
(sizeof(int *) - 1)));
lws_dll2_add_tail(&s.best->list,
&dsh->oha[kind].owner);
}
assert(s.dsh->locally_free >= s.best->asize);
s.dsh->locally_free -= s.best->asize;
s.dsh->locally_in_use += s.best->asize;
dsh->oha[kind].total_size += s.best->asize;
assert(s.dsh->locally_in_use <= s.dsh->buffer_size);
} else {
lws_dsh_obj_t *nf;
#if defined(_DEBUG)
uint8_t *e = ((uint8_t *)s.best) + s.best->asize;
#endif
/*
* Free area was oversize enough that we need to split it.
*
* Unlink the free area and move its header forward to account
* for our usage of its start area. It's like this so that we
* can coalesce sequential objects.
*/
//lwsl_notice("%s: splitting... free reduce %zu -> %zu\n",
// __func__, s.best->asize, s.best->asize - asize);
assert(s.best->asize >= asize);
/* unlink the entire original hole object at s.best */
lws_dll2_remove(&s.best->list);
s.dsh->locally_free -= s.best->asize;
s.dsh->locally_in_use += asize;
/* latter part becomes new hole object */
nf = (lws_dsh_obj_t *)(((uint8_t *)s.best) + asize);
assert((uint8_t *)nf < e);
memset(nf, 0, sizeof(*nf));
nf->asize = s.best->asize - asize; /* rump free part only */
assert(((uint8_t *)nf) + nf->asize <= e);
lws_dll2_add_sorted(&nf->list, &s.dsh->oha[0].owner, buf_compare);
s.dsh->locally_free += s.best->asize;
/* take over s.best as the new allocated object, fill it in */
s.best->dsh = s.dsh;
s.best->kind = kind;
s.best->size = size1 + size2;
s.best->asize = asize;
// lwsl_notice("%s: split off kind %d\n", __func__, kind);
assert((uint8_t *)s.best + s.best->asize < e);
assert((uint8_t *)s.best + s.best->asize <= (uint8_t *)nf);
if (size1)
memcpy(&s.best[1], src1, size1);
if (src2)
memcpy((uint8_t *)&s.best[1] + size1, src2, size2);
if (replace) {
s.best->list.prev = replace->prev;
s.best->list.next = replace->next;
s.best->list.owner = replace->owner;
if (replace->prev)
replace->prev->next = &s.best->list;
if (replace->next)
replace->next->prev = &s.best->list;
} else
if (dsh) {
assert(!(((unsigned long)(intptr_t)(s.best)) &
(sizeof(int *) - 1)));
lws_dll2_add_tail(&s.best->list,
&dsh->oha[kind].owner);
}
assert(s.dsh->locally_free >= asize);
dsh->oha[kind].total_size += asize;
assert(s.dsh->locally_in_use <= s.dsh->buffer_size);
}
// lws_dsh_describe(dsh, "post-alloc");
return 0;
}
int
lws_dsh_alloc_tail(lws_dsh_t *dsh, int kind, const void *src1, size_t size1,
const void *src2, size_t size2)
{
int r;
do {
size_t s1 = size1, s2 = size2;
if (!dsh->splitat || !(dsh->flags & LWS_DSHFLAG_ENABLE_SPLIT)) {
s1 = size1;
s2 = size2;
} else
if (s1 > dsh->splitat) {
s1 = dsh->splitat;
s2 = 0;
} else {
if (s1 + s2 > dsh->splitat)
s2 = dsh->splitat - s1;
}
r = _lws_dsh_alloc_tail(dsh, kind, src1, s1, src2, s2, NULL);
if (r)
return r;
src1 = (void *)((uint8_t *)src1 + s1);
src2 = (void *)((uint8_t *)src2 + s2);
size1 -= s1;
size2 -= s2;
} while (size1 + size2);
return 0;
}
void
lws_dsh_consume(struct lws_dsh *dsh, int kind, size_t len)
{
lws_dsh_obj_t *h = (lws_dsh_obj_t *)dsh->oha[kind + 1].owner.head;
assert(len <= h->size);
assert(h->pos + len <= h->size);
if (len == h->size || h->pos + len == h->size) {
lws_dsh_free((void **)&h);
return;
}
assert(0);
h->pos += len;
}
void
lws_dsh_free(void **pobj)
{
lws_dsh_obj_t *_o = (lws_dsh_obj_t *)((uint8_t *)(*pobj) - sizeof(*_o)),
*_o2;
lws_dsh_t *dsh = _o->dsh;
/* anything coming out of here must be aligned */
assert(!(((size_t)(intptr_t)_o) & (sizeof(int *) - 1)));
/*
* Remove the object from its list and place on the free list of the
* dsh the buffer space belongs to
*/
lws_dll2_remove(&_o->list);
*pobj = NULL;
assert(dsh->locally_in_use >= _o->asize);
dsh->locally_free += _o->asize;
dsh->locally_in_use -= _o->asize;
assert(dsh->oha[_o->kind].total_size >= _o->asize);
dsh->oha[_o->kind].total_size -= _o->asize; /* account for usage by kind */
assert(dsh->locally_in_use <= dsh->buffer_size);
/*
* The free space list is sorted in buffer address order, so detecting
* coalescing opportunities is cheap. Because the free list should be
* continuously tending to reduce by coalescing, the sorting should not
* be expensive to maintain.
*/
_o->size = 0; /* not meaningful when on free list */
lws_dll2_add_sorted(&_o->list, &_o->dsh->oha[0].owner, buf_compare);
/* First check for already-free block at the end we can subsume.
* Because the free list is sorted, if there is such a guy he is
* already our list.next */
_o2 = (lws_dsh_obj_t *)_o->list.next;
if (_o2 && (uint8_t *)_o + _o->asize == (uint8_t *)_o2) {
/*
* since we are freeing _obj, we can coalesce with a
* free area immediately ahead of it
*
* [ _o (being freed) ][ _o2 (free) ] -> [ larger _o ]
*/
_o->asize += _o2->asize;
/* guy next to us was absorbed into us */
lws_dll2_remove(&_o2->list);
}
/* Then check if we can be subsumed by a free block behind us.
* Because the free list is sorted, if there is such a guy he is
* already our list.prev */
_o2 = (lws_dsh_obj_t *)_o->list.prev;
if (_o2 && (uint8_t *)_o2 + _o2->asize == (uint8_t *)_o) {
/*
* since we are freeing obj, we can coalesce it with
* the previous free area that abuts it
*
* [ _o2 (free) ][ _o (being freed) ] -> [ larger _o2 ]
*/
_o2->asize += _o->asize;
/* we were absorbed! */
lws_dll2_remove(&_o->list);
}
// lws_dsh_describe(dsh, "post-alloc");
}
int
lws_dsh_get_head(lws_dsh_t *dsh, int kind, void **obj, size_t *size)
{
lws_dsh_obj_t *_obj;
if (!dsh)
return 1;
_obj = (lws_dsh_obj_t *)lws_dll2_get_head(&dsh->oha[kind + 1].owner);
if (!_obj) {
*obj = 0;
*size = 0;
return 1; /* there is no head */
}
*obj = (void *)(&_obj[1]);
*size = _obj->size;
/* anything coming out of here must be aligned */
assert(!(((unsigned long)(intptr_t)(*obj)) & (sizeof(int *) - 1)));
return 0; /* we returned the head */
}
#if defined(_DEBUG) && !defined(LWS_WITH_NO_LOGS)
static int
describe_kind(struct lws_dll2 *d, void *user)
{
lws_dsh_obj_t *obj = lws_container_of(d, lws_dsh_obj_t, list);
lwsl_notice(" _obj %p - %p, dsh %p, size %zu, asize %zu\n",
obj, (uint8_t *)obj + obj->asize,
obj->dsh, obj->size, obj->asize);
return 0;
}
void
lws_dsh_describe(lws_dsh_t *dsh, const char *desc)
{
int n = 0;
lwsl_notice("%s: dsh %p, bufsize %zu, kinds %d, lf: %zu, liu: %zu, %s\n",
__func__, dsh, dsh->buffer_size, dsh->count_kinds,
dsh->locally_free, dsh->locally_in_use, desc);
for (n = 0; n < dsh->count_kinds; n++) {
lwsl_notice(" Kind %d:\n", n);
lws_dll2_foreach_safe(&dsh->oha[n].owner, dsh, describe_kind);
}
}
#endif

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More