pebble/src/fw/services/normal/settings/settings_file.h
Josh Soref a35061eaf8 spelling: iteration
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2025-01-29 00:03:25 -05:00

168 lines
7.6 KiB
C

/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "settings_raw_iter.h"
// Deleted records have their key stick around for at least DELETED_LIFETIME
// before they can be garbage collected from the file in which they are
// contained, that way they have time to propegate to all devices we end up
// syncronizing with. For more information, refer to the sync protocol proposal:
// https://pebbletechnology.atlassian.net/wiki/pages/viewpage.action?pageId=26837564
//
// FIXME: See PBL-18945
#define DELETED_LIFETIME (0 * SECONDS_PER_DAY)
//! A SettingsFile is just a simple binary key-value store. Keys can be strings,
//! uint32_ts, or arbitrary bytes. Values are similarilly flexible. All
//! operations are atomic, so a reboot in the middle of changing the value for a
//! key will always either complete, returning the new value upon reboot, or
//! will just return the old value.
//! It also supports bidirectional syncronization between the phone & watch,
//! using timestamps to resolve conflicts.
//! Note that although all operations are atomic, they are not thread-safe. If
//! you will be accessing a SettingsFile from multiple threads, make sure you
//! use locks!
// NOTE: These fields are internal, modify them at your own risk!
typedef struct SettingsFile {
SettingsRawIter iter;
char *name;
//! Maximum total space which can be used by this settings_file before a
//! compaction will be forced. (Must be >= max_used_space)
int max_space_total;
//! Maximum space that can be used by valid records within this settings_file.
//! Once this has been exceeded, attempting to add more keys or values will
//! fail.
int max_used_space;
//! Amount of space in the settings_file that is currently dead, i.e.
//! has been written to with some data, but that data is no longer valid.
//! (overwritten records get added to this)
int dead_space;
//! Amount of space in the settings_file that is currently used by valid
//! records.
int used_space;
//! When this file as a whole was last_modified.
//! Defined as records.max(&:last_modified)
uint32_t last_modified;
//! The position of the current record in the iteration (if any). Necessary
//! so that clients can read other records in the middle of iteration (i.e.
//! settings_file_each()/settings_file_rewrite()), without messing up the
//! state of the iteration. Set to 0 if not in use.
int cur_record_pos;
} SettingsFile;
//! max_used_space should be >= 5317 for persist files to make sure we can
//! always fit all of the records in the worst case (if the programmer stored
//! nothing but booleans).
//! Note: If the settings file already exists, the max_used_space parameter is
//! ignored. We could change this if the need arises.
status_t settings_file_open(SettingsFile *file, const char *name,
int max_used_space);
void settings_file_close(SettingsFile *file);
bool settings_file_exists(SettingsFile *file, const void *key, size_t key_len);
status_t settings_file_delete(SettingsFile *file,
const void *key, size_t key_len);
int settings_file_get_len(SettingsFile *file, const void *key, size_t key_len);
//! val_out_len must exactly match the length of the record on disk.
status_t settings_file_get(SettingsFile *file, const void *key, size_t key_len,
void *val_out, size_t val_out_len);
status_t settings_file_set(SettingsFile *file, const void *key, size_t key_len,
const void *val, size_t val_len);
//! Mark a record as synced. The flag will remain until the record is overwritten
//! @param file the settings_file that contains the record
//! @param key the key to the settings file. Note: keys can be up to 127 bytes
//! @param key_len the length of the key
status_t settings_file_mark_synced(SettingsFile *file, const void *key, size_t key_len);
//! set a byte in a setting. This can only be used a byte at a time to guarantee
//! atomicity. Do not use to modify several bytes in a row!
//! Note that only the reset bits will be applied (it writes flash directly)
status_t settings_file_set_byte(
SettingsFile *file, const void *key, size_t key_len,
size_t offset, uint8_t byte);
//////////////////
// Each/rewrite //
//////////////////
typedef void (*SettingsFileGetter)(SettingsFile *file,
void *buf, size_t buf_len);
typedef struct {
uint32_t last_modified;
SettingsFileGetter get_key;
int key_len;
SettingsFileGetter get_val;
int val_len;
bool dirty; // has the dirty flag set
} SettingsRecordInfo;
//! Callback used for using settings_file_each.
//! The bool returned is used to control the iteration.
//! - If a callback returns true, the iteration continues
//! - If a callback returns false, the iteration stops.
typedef bool (*SettingsFileEachCallback)(SettingsFile *file,
SettingsRecordInfo *info,
void *context);
//! Calls cb for each and every entry within the given file.
//! Note that you cannot modify the settings file while iterating. If you want
//! to do this, try settings_file_rewrite instead. (you can read other entries
//! without fault).
status_t settings_file_each(SettingsFile *file, SettingsFileEachCallback cb,
void *context);
typedef void (*SettingsFileRewriteCallback)(SettingsFile *old_file,
SettingsFile *new_file,
SettingsRecordInfo *info,
void *context);
//! Opens a new SettingsFile with the same name as the original SettingsFile,
//! in overwrite mode. This new file is passed into the given
//! SettingsFileRewriteCallback, which is called for each entry within the
//! original file. If you desire to preserve a key/value pair, you must write
//! it to the new file.
status_t settings_file_rewrite(SettingsFile *file,
SettingsFileRewriteCallback cb,
void *context);
//! Callback used for using settings_file_rewrite_filtered.
//! The bool returned is used to control whether or not the record is included in the file
//! after compaction. This callback is not allowed to use any other settings_file calls.
//! - If callback returns true, the record is included
//! - If callback returns false, the record is not included
typedef bool (*SettingsFileRewriteFilterCallback)(void *key, size_t key_len, void *value,
size_t value_len, void *context);
//! Opens a new SettingsFile with the same name as the original SettingsFile,
//! in overwrite mode. Any records from the old file which pass through the filter_cb with
//! a true result are included into the new file. This call is much faster than using
//! settings_file_rewrite if all you are doing is excluding specific records from the old file.
status_t settings_file_rewrite_filtered(SettingsFile *file,
SettingsFileRewriteFilterCallback filter_cb, void *context);