nuttx/fs/mnemofs/mnemofs.h
Saurav Pal daa3168cfb fs/mnemofs: Fix journal log rw issue, read size issue
Fixes the journal log read and write size and overlap issues, along with read return value issue.

Signed-off-by: Saurav Pal <resyfer.dev@gmail.com>
2024-08-17 09:10:04 -03:00

1909 lines
57 KiB
C

/****************************************************************************
* fs/mnemofs/mnemofs.h
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you 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.
*
* Alternatively, the contents of this file may be used under the terms of
* the BSD-3-Clause license:
*
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (c) 2024 Saurav Pal
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. Neither the name of the author nor the names of its contributors may
* be used to endorse or promote products derived from this software
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
*
****************************************************************************/
#ifndef __FS_MNEMOFS_MNEMOFS_H
#define __FS_MNEMOFS_MNEMOFS_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <debug.h>
#include <nuttx/fs/fs.h>
#include <nuttx/list.h>
#include <nuttx/mtd/mtd.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define MFS_JRNL_MAGIC "-mfs!j!-"
#define MFS_MN_MAGIC "-mfs!m!-"
#define MFS_CEILDIVIDE(num, denom) (((num) + ((denom) - 1)) / (denom))
#define MFS_UPPER8(num) (((num) + 7) & (-8))
#define MFS_BLK2PG(sb, blk) ((blk) << (sb)->log_pg_in_blk)
#define MFS_PG2BLK(sb, pg) ((pg) >> (sb)->log_pg_in_blk)
#define MFS_PG2BLKPGOFF(sb, pg) ((pg) % (1 << (sb)->log_pg_in_blk))
#define MFS_LOCK(sb) ((sb)->fs_lock)
#define MFS_PGSZ(sb) ((sb)->pg_sz)
#define MFS_LRU(sb) ((sb)->lru)
#define MFS_BLKSZ(sb) ((sb)->blk_sz)
#define MFS_MN(sb) ((sb)->mn)
#define MFS_JRNL(sb) ((sb)->j_state)
#define MFS_LOGPGSZ(sb) (MFS_CEILDIVIDE((sb)->log_pg_sz, 8))
#define MFS_PGINBLK(sb) ((sb)->pg_in_blk)
#define MFS_MTD(sb) ((sb)->drv->u.i_mtd)
#define MFS_RWBUF(sb) ((sb)->rw_buf)
#define MFS_BA(sb) ((sb)->ba_state)
#define MFS_NBLKS(sb) ((sb)->n_blks)
#define MFS_OFILES(sb) ((sb)->of)
#define MFS_FLUSH(sb) ((sb)->flush)
#define MFS_NPGS(sb) (MFS_NBLKS(sb) * MFS_PGINBLK(sb))
#define MFS_HASHSZ 16
#define MFS_CTZ_SZ(l) ((l)->sz)
#define MFS_DIRENTSZ(dirent) ((2 * 2) + 4 + (16 * 3) + 8 + 1 \
+ (dirent)->namelen)
#define MFS_JRNL_LIM(sb) (MFS_JRNL(sb).n_blks / 2)
#define MFS_TRAVERSE_INITSZ 8
/****************************************************************************
* Public Types
****************************************************************************/
typedef uint32_t mfs_t;
enum
{
MFS_PG_USED,
MFS_PG_FREE,
MFS_BLK_BAD,
MFS_BLK_ERASABLE,
MFS_BLK_FREE,
MFS_BLK_USED,
};
enum MFS_PATH_FLAGS
{
MFS_ISDIR = (1 << 0), /* Path is a directory. */
MFS_ISFILE = (1 << 1), /* Path is a file. */
MFS_EXIST = (1 << 2), /* Path Exists */
MFS_FINPATH = (1 << 3), /* File in midele of path before bottom most
* child. Not reachable.
*/
MFS_P_EXIST = (1 << 4), /* Parent of the bottom most element exists. */
MFS_P_ISDIR = (1 << 5), /* Parent is a directory. */
};
struct mfs_ctz_s
{
mfs_t pg_e;
mfs_t idx_e;
};
struct mfs_path_s
{
struct mfs_ctz_s ctz;
mfs_t off;
mfs_t sz;
};
struct mfs_ba_state_s
{
mfs_t s_blk; /* Start block */
mfs_t c_pg; /* Current page */
FAR mfs_t *k_del; /* Delete counter for blocks. */
size_t n_bmap_upgs;
FAR uint8_t *bmap_upgs; /* Bitmap of used pages. */
};
struct mfs_mn_s
{
mfs_t pg; /* Only mblk1's pg will be used here. */
mfs_t jrnl_blk; /* Start of journal. */
mfs_t mblk_idx; /* Index for next MN entry inside blk. */
struct mfs_ctz_s root_ctz;
mfs_t root_sz;
struct timespec ts;
mode_t root_mode;
struct timespec root_st_atim;
struct timespec root_st_ctim;
struct timespec root_st_mtim;
};
struct mfs_jrnl_state_s
{
mfs_t mblk1;
mfs_t mblk2;
mfs_t n_logs;
mfs_t log_cpg; /* Current (last) page */
mfs_t log_cblkidx; /* Current (last) block index. */
mfs_t log_spg; /* First log's page */
mfs_t log_sblkidx; /* First jrnl blk index. TODO: jrnlarr > 1 blk. */
mfs_t jrnlarr_pg;
mfs_t jrnlarr_pgoff;
uint16_t n_blks; /* TODO: Does not include the master node. */
};
struct mfs_sb_s
{
FAR uint8_t *rw_buf;
FAR struct inode *drv;
mutex_t fs_lock;
mfs_t sb_blk; /* Block number of the superblock */
mfs_t pg_sz;
uint8_t log_pg_sz;
mfs_t blk_sz;
uint8_t log_blk_sz;
mfs_t n_blks;
uint8_t log_n_blks;
uint16_t pg_in_blk;
uint8_t log_pg_in_blk;
struct mfs_mn_s mn; /* Master Node */
struct mfs_jrnl_state_s j_state; /* Journal State */
struct mfs_ba_state_s ba_state; /* Block Allocator State */
struct list_node lru;
struct list_node of; /* open files. */
bool flush;
};
/* This is for *dir VFS methods. */
struct mfs_fsdirent
{
struct fs_dirent_s base; /* VFS directory structure */
uint8_t idx; /* This only goes from 0 for ., 1 for .. and
* 2 for others.
*/
FAR struct mfs_pitr_s *pitr;
FAR struct mfs_path_s *path;
mfs_t depth;
};
/* LRU Delta */
struct mfs_delta_s
{
struct list_node list;
mfs_t off;
mfs_t n_b;
FAR char *upd;
};
/* LRU Node */
struct mfs_node_s
{
struct list_node list;
struct list_node delta;
mfs_t n_list;
mfs_t depth;
mfs_t sz;
mfs_t range_min;
mfs_t range_max;
struct timespec st_mtim;
struct timespec st_atim;
struct timespec st_ctim;
FAR struct mfs_path_s *path;
};
/* Common Part Open File Descriptor */
struct mfs_ocom_s
{
bool new_ent;
mfs_t sz; /* Current file size. */
mfs_t off;
mfs_t depth;
uint8_t refcount;
int oflags;
FAR struct mfs_path_s *path;
};
/* Open part for file descriptors. */
struct mfs_ofd_s
{
struct list_node list;
FAR struct mfs_ocom_s *com;
};
struct mfs_dirent_s
{
uint16_t name_hash; /* Should be at start to improve efficiency. */
uint16_t mode;
mfs_t sz;
struct timespec st_atim; /* Time of last access */
struct timespec st_mtim; /* Time of last modification */
struct timespec st_ctim; /* Time of last status change */
struct mfs_ctz_s ctz;
uint8_t namelen;
FAR char name[];
};
/* Parent iterator */
struct mfs_pitr_s
{
struct mfs_path_s p; /* Parent representation */
mfs_t depth;
mfs_t c_off; /* Current offset. */
};
/* TODO: depth >= 1 */
/* IMP TODO: sizeof(x) != size of buffer required to store it. Need to fix. */
/****************************************************************************
* Public Data
****************************************************************************/
#ifdef __cplusplus
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
/****************************************************************************
* Inline Functions
****************************************************************************/
/****************************************************************************
* Name: mfs_blkremsz
*
* Description:
* Given a page and a page offset, it returns the bytes left in the entire
* block after the offset location.
*
* Input Parameters:
* sb - Superblock instance of the device.
* pg - Page number.
* pgoff - Page offset.
*
* Returned Value:
* Bytes left in the block.
*
****************************************************************************/
static mfs_t inline mfs_blkremsz(FAR const struct mfs_sb_s * const sb,
mfs_t pg, mfs_t pgoff)
{
return MFS_BLKSZ(sb) - (MFS_PG2BLKPGOFF(sb, pg) * sb->pg_sz + pgoff);
}
static inline mfs_t mfs_ctz(const uint32_t x)
{
if (predict_false(x == 0))
{
/* Special case, since we're using this for the CTZ skip list. The 0th
* block has no pointers.
*/
return 0;
}
#if defined(__GNUC__)
return __builtin_ctz(x);
#else
uint32_t c;
/* Credits:
* http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightBinSearch
*/
if (x & 0x1)
{
/* special case for odd x (assumed to happen half of the time) */
c = 0;
}
else
{
c = 1;
if ((x & 0xffff) == 0)
{
x >>= 16;
c += 16;
}
if ((x & 0xff) == 0)
{
x >>= 8;
c += 8;
}
if ((x & 0xf) == 0)
{
x >>= 4;
c += 4;
}
if ((x & 0x3) == 0)
{
x >>= 2;
c += 2;
}
c -= x & 0x1;
}
return c;
#endif
}
static inline mfs_t mfs_clz(const uint32_t x)
{
if (predict_false(x == UINT32_MAX))
{
/* Special case, since we're using this for the CTZ skip list. The 0th
* block has no pointers.
*/
return 0;
}
#if defined(__GNUC__)
return __builtin_clz(x);
#else
return 0; /* TODO */
#endif
}
static inline mfs_t mfs_popcnt(mfs_t x)
{
#if defined(__GNUC__)
return __builtin_popcount(x);
#else
/* Can be found at:
* http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan
*/
mfs_t c;
for (c = 0; x; c++)
{
x &= x - 1;
}
#endif
}
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
/* mnemofs.c */
int mnemofs_flush(FAR struct mfs_sb_s *sb);
/* mnemofs_journal.c */
/****************************************************************************
* Name: mfs_jrnl_init
*
* Description:
* Initialize journal if device is already formatted.
*
* Input Parameters:
* sb - Superblock instance of the device.
* blk - First block of the journal.
*
* Returned Value:
* 0 - OK
* < 0 - Error
*
****************************************************************************/
int mfs_jrnl_init(FAR struct mfs_sb_s * const sb, mfs_t blk);
/****************************************************************************
* Name: mfs_jrnl_fmt
*
* Description:
* Format a journal to the device.
*
* Input Parameters:
* sb - Superblock instance of the device.
* blk1 - First master block for the journal.
* blk2 - Second master block for the journal.
*
* Returned Value:
* 0 - OK
* < 0 - Error
*
* Assumptions/Limitations:
* If blk1 == 0 and blk2 == 0, this means that this will also format in the
* master blocks. If this is not satisfied, the provided values will be
* taken to denote the master nodes.
*
****************************************************************************/
int mfs_jrnl_fmt(FAR struct mfs_sb_s * const sb, mfs_t *blk1, mfs_t *blk2,
FAR mfs_t *jrnl_blk);
/****************************************************************************
* Name: mfs_jrnl_free
*
* Description:
* Free the journal.
*
* Input Parameters:
* sb - Superblock instance of the device.
*
****************************************************************************/
void mfs_jrnl_free(FAR struct mfs_sb_s * const sb);
/****************************************************************************
* Name: mfs_jrnl_blkidx2blk
*
* Description:
* Gets the block number of a journal block at an index.
*
* Input Parameters:
* sb - Superblock instance of the device.
* blk_idx - Index of the block who's block number to find.
*
* Returned Value:
* Block number.
*
* Assumptions/Limitations:
* Assumes valid index. This index can also include master blocks. Also
* assumes, for now, that the entire journal array can fit in the first
* block.
*
****************************************************************************/
mfs_t mfs_jrnl_blkidx2blk(FAR const struct mfs_sb_s * const sb,
const mfs_t blk_idx);
/****************************************************************************
* Name: mfs_jrnl_updatedinfo
*
* Description:
* Update the path information from the journal.
*
* Input Parameters:
* sb - Superblock instance of the device.
* path - "Base state" path.
* depth - Path depth.
*
* Returned Value:
* 0 - OK
* < 0 - Error
*
* Assumptions/Limitations:
* This applies updates over data that is already gathered from the data
* section of the flash. The data section is the "base state" over which
* the updates in the journal are applied.
*
****************************************************************************/
int mfs_jrnl_updatedinfo(FAR const struct mfs_sb_s * const sb,
FAR struct mfs_path_s * const path,
const mfs_t depth);
/****************************************************************************
* Name: mfs_jrnl_wrlog
*
* Description:
* Write a log for LRU node when its popped from the LRU.
*
* Input Parameters:
* sb - Superblock instance of the device.
* node - LRU node.
* loc_new - New location of the CTZ skip list.
* sz_new - New size of the CTZ skip list.
*
* Returned Value:
* 0 - OK
* < 0 - Error
*
* Assumptions/Limitations:
* When a node from LRU is flushed, it's written to the data in any space
* that is available according to the block allocator, and the new location
* is recorded as a log along with the old location. Any log of this same
* CTZ skip list in the journal will use this "snapshot" of the location,
* ie. the updated path will be used until another log of the same CTZ
* skip list is stored, after which the path will be updated again, and
* so on.
*
****************************************************************************/
int mfs_jrnl_wrlog(FAR struct mfs_sb_s * const sb,
FAR const struct mfs_node_s *node,
const struct mfs_ctz_s loc_new, const mfs_t sz_new);
/****************************************************************************
* Name: mfs_jrnl_flush
*
* Description:
* Flush the entire journal. This is the entry point of the entire flush
* operation and includes messing with the master node as well.
*
* Input Parameters:
* sb - Superblock instance of the device.
*
* Returned Value:
* 0 - OK
* < 0 - Error
*
****************************************************************************/
int mfs_jrnl_flush(FAR struct mfs_sb_s * const sb);
/****************************************************************************
* Name: mfs_jrnl_isempty
*
* Description:
* Check if the journal is empty.
*
* Input Parameters:
* sb - Superblock instance of the device.
*
* Returned Value:
* True is the journal is empty, false otherwise.
*
****************************************************************************/
bool mfs_jrnl_isempty(FAR const struct mfs_sb_s * const sb);
/* mnemofs_blkalloc.c */
/****************************************************************************
* Name: mfs_ba_init
*
* Description:
* Formats and initializes the block allocator.
*
* Input Parameters:
* sb - Superblock instance of the device.
*
* Returned Value:
* 0 - OK
* -ENOMEM - No memory left.
*
****************************************************************************/
int mfs_ba_fmt(FAR struct mfs_sb_s * const sb);
/****************************************************************************
* Name: mfs_ba_init
*
* Description:
* Initializes the block allocator.
*
* Input Parameters:
* sb - Superblock instance of the device.
*
* Returned Value:
* 0 - OK
* -ENOMEM - No memory left.
*
****************************************************************************/
int mfs_ba_init(FAR struct mfs_sb_s * const sb);
/****************************************************************************
* Name: mfs_ba_getpg
*
* Description:
* Returns an allocated page.
*
* Input Parameters:
* sb - Superblock instance of the device.
*
* Returned Value:
* 0 - No more space left
* > 0 - Page number
*
* Assumptions/Limitations:
* This assumes a locked environment when called.
*
****************************************************************************/
mfs_t mfs_ba_getpg(FAR struct mfs_sb_s * const sb);
/****************************************************************************
* Name: mfs_ba_getblk
*
* Description:
* Returns an allocated block.
*
* Input Parameters:
* sb - Superblock instance of the device.
*
* Returned Value:
* 0 - No more space left
* > 0 - Block number
*
* Assumptions/Limitations:
* This assumes a locked environment when called.
*
****************************************************************************/
mfs_t mfs_ba_getblk(FAR struct mfs_sb_s * const sb);
/****************************************************************************
* Name: mfs_ba_pgmarkdel
*
* Description:
* Mark a page as being ready for deletion.
*
* Input Parameters:
* sb - Superblock instance of the device.
* pg - Page number.
*
* Assumptions/Limitations:
* This assumes a locked environment when called.
*
****************************************************************************/
void mfs_ba_pgmarkdel(FAR struct mfs_sb_s * const sb, mfs_t pg);
/****************************************************************************
* Name: mfs_ba_blkmarkdel
*
* Description:
* Mark a block as being ready for deletion.
*
* Input Parameters:
* sb - Superblock instance of the device.
* blk - Block number.
*
* Assumptions/Limitations:
* This assumes a locked environment when called.
*
****************************************************************************/
void mfs_ba_blkmarkdel(FAR struct mfs_sb_s * const sb, mfs_t blk);
/****************************************************************************
* Name: mfs_ba_delmarked
*
* Description:
* Delete all marked for deletion blocks.
*
* Input Parameters:
* sb - Superblock instance of the device.
*
* Returned Value:
* 0 - OK
* < 0 - Error
*
* Assumptions/Limitations:
* This assumes a locked environment when called.
*
****************************************************************************/
int mfs_ba_delmarked(FAR struct mfs_sb_s * const sb);
/****************************************************************************
* Name: mfs_ba_markusedpg
*
* Description:
* Marked page as being used.
*
* Input Parameters:
* sb - Superblock instance of the device.
* pg - Page number
*
* Assumptions/Limitations:
* This assumes a locked environment when called.
*
****************************************************************************/
void mfs_ba_markusedpg(FAR struct mfs_sb_s * const sb, mfs_t pg);
/****************************************************************************
* Name: mfs_ba_markusedblk
*
* Description:
* Marked block as being used.
*
* Input Parameters:
* sb - Superblock instance of the device.
* blk - Block number
*
* Assumptions/Limitations:
* This assumes a locked environment when called.
*
****************************************************************************/
void mfs_ba_markusedblk(FAR struct mfs_sb_s * const sb, mfs_t blk);
/****************************************************************************
* Name: mfs_ba_getavailpgs
*
* Description:
* Get number of available pages.
*
* Input Parameters:
* sb - Superblock instance of the device.
* pg - Page number
*
* Assumptions/Limitations:
* This assumes a locked environment when called.
*
****************************************************************************/
mfs_t mfs_ba_getavailpgs(FAR const struct mfs_sb_s * const sb);
/****************************************************************************
* Name: mfs_ba_free
*
* Description:
* Free the block allocator
*
* Input Parameters:
* sb - Superblock instance of the device.
*
* Assumptions/Limitations:
* This assumes a locked environment when called.
*
****************************************************************************/
void mfs_ba_free(FAR struct mfs_sb_s * const sb);
/* mnemofs_rw.c */
/****************************************************************************
* Name: mfs_isbadblk
*
* Description:
* Is a block bad.
*
* Input Parameters:
* sb - Superblock instance of the device.
* blk - Block Number
*
* Assumptions/Limitations:
* This assumes a locked environment when called.
*
****************************************************************************/
int mfs_isbadblk(FAR const struct mfs_sb_s * const sb, mfs_t blk);
/****************************************************************************
* Name: mfs_markbadblk
*
* Description:
* Mark a block as bad.
*
* Input Parameters:
* sb - Superblock instance of the device.
* blk - Block Number
*
* Assumptions/Limitations:
* This assumes a locked environment when called.
*
****************************************************************************/
int mfs_markbadblk(FAR const struct mfs_sb_s * const sb, mfs_t blk);
/****************************************************************************
* Name: mfs_write_page
*
* Description:
* Write a page.
*
* Input Parameters:
* sb - Superblock instance of the device.
* data - Buffer
* datalen - Length of buffer.
* pg - Page number.
* pgoff - Offset into the page.
*
* Assumptions/Limitations:
* This assumes a locked environment when called.
*
****************************************************************************/
ssize_t mfs_write_page(FAR const struct mfs_sb_s * const sb,
FAR const char *data, const mfs_t datalen,
const off_t page, const mfs_t pgoff);
/****************************************************************************
* Name: mfs_read_page
*
* Description:
* Read a page.
*
* Input Parameters:
* sb - Superblock instance of the device.
* data - Buffer
* datalen - Length of buffer.
* pg - Page number.
* pgoff - Offset into the page.
*
* Assumptions/Limitations:
* This assumes a locked environment when called.
*
****************************************************************************/
ssize_t mfs_read_page(FAR const struct mfs_sb_s * const sb,
FAR char *data, const mfs_t datalen, const off_t page,
const mfs_t pgoff);
/****************************************************************************
* Name: mfs_erase_blk
*
* Description:
* Erase a block.
*
* Input Parameters:
* sb - Superblock instance of the device.
* blk - Block Number
*
* Assumptions/Limitations:
* This assumes a locked environment when called.
*
****************************************************************************/
int mfs_erase_blk(FAR const struct mfs_sb_s * const sb, const off_t blk);
/****************************************************************************
* Name: mfs_erase_nblks
*
* Description:
* Erase consecutive blocks.
*
* Input Parameters:
* sb - Superblock instance of the device.
* blk - Block Number
*
* Assumptions/Limitations:
* This assumes a locked environment when called.
*
****************************************************************************/
int mfs_erase_nblks(FAR const struct mfs_sb_s * const sb, const off_t blk,
const size_t n);
/* mnemofs_util.c */
/****************************************************************************
* Name: mfs_arrhash
*
* Description:
* Returns an 8-bit hash of an entire array of data.
*
* Input Parameters:
* arr - Data array.
* len - Length of the array.
*
* Returned Value:
* 16-bit hash of the array.
*
****************************************************************************/
uint8_t mfs_arrhash(FAR const char *arr, ssize_t len);
/* TODO: Put below in place of above. */
uint16_t mfs_hash(FAR const char *arr, ssize_t len);
/****************************************************************************
* Name: mfs_ser_8
*
* Description:
* Serialize a 8 bit type into output.
*
* Input Parameters:
* n - 8 bit to serialize
* out - Output array where to serialize.
*
* Returned Value:
* Pointer to byte after the end of serialized value.
*
****************************************************************************/
FAR char *mfs_ser_8(const uint8_t n, FAR char * const out);
/****************************************************************************
* Name: mfs_deser_8
*
* Description:
* Deserialize a 8 bit type from input.
*
* Input Parameters:
* in - Input array from where to deserialize.
* n - 8 bit to deserialize
*
* Returned Value:
* Pointer to byte after the end of serialized value.
*
****************************************************************************/
FAR const char *mfs_deser_8(FAR const char * const in, uint8_t *n);
/****************************************************************************
* Name: mfs_ser_str
*
* Description:
* Serialize a string into output.
*
* Input Parameters:
* str - String to serialize
* len - Length of string
* out - Output array where to serialize.
*
* Returned Value:
* Pointer to byte after the end of serialized value.
*
****************************************************************************/
FAR char *mfs_ser_str(FAR const char * const str, const mfs_t len,
FAR char * const out);
/****************************************************************************
* Name: mfs_deser_str
*
* Description:
* Deserialize a string from intput.
*
* Input Parameters:
* in - Intput array from where to deserialize.
* str - String to deserialize
* len - Length of string
*
* Returned Value:
* Pointer to byte after the end of serialized value.
*
****************************************************************************/
FAR const char *mfs_deser_str(FAR const char * const in,
FAR char * const str, const mfs_t len);
/****************************************************************************
* Name: mfs_ser_mfs
*
* Description:
* Serialize a mfs_t type into output.
*
* Input Parameters:
* n - mfs_t to serialize
* out - Output array where to serialize.
*
* Returned Value:
* Pointer to byte after the end of serialized value.
*
****************************************************************************/
FAR char *mfs_ser_mfs(const mfs_t n, FAR char * const out);
/****************************************************************************
* Name: mfs_deser_mfs
*
* Description:
* Deserialize a mfs_t type from input..
*
* Input Parameters:
* in - Input array from where to deserialize.
* n - mfs_t to deserialize
*
* Returned Value:
* Pointer to byte after the end of serialized value.
*
****************************************************************************/
FAR const char *mfs_deser_mfs(FAR const char * const in,
FAR mfs_t * const n);
/****************************************************************************
* Name: mfs_ser_ctz
*
* Description:
* Serialize a mfs_ctz_store_s type into output.
*
* Input Parameters:
* x - mfs_ctz_s to serialize
* out - Output array where to serialize.
*
* Returned Value:
* Pointer to byte after the end of serialized value.
*
****************************************************************************/
FAR char *mfs_ser_ctz(FAR const struct mfs_ctz_s * const x,
FAR char * const out);
/****************************************************************************
* Name: mfs_deser_ctz
*
* Description:
* Deserialize a mfs_ctz_store_s type into output.
*
* Input Parameters:
* in - Input array from where to deserialize.
* x - mfs_ctz_s to deserialize
*
* Returned Value:
* Pointer to byte after the end of serialized value.
*
****************************************************************************/
FAR const char *mfs_deser_ctz(FAR const char * const in,
FAR struct mfs_ctz_s * const x);
FAR char *mfs_ser_path(FAR const struct mfs_path_s * const x,
FAR char * const out);
FAR const char *mfs_deser_path(FAR const char * const in,
FAR struct mfs_path_s * const x);
/****************************************************************************
* Name: mfs_ser_timespec
*
* Description:
* Serialize timespec.
*
* Input Parameters:
* x - Value to serialize
* out - Output array where to serialize.
*
* Returned Value:
* Pointer to byte after the end of serialized value.
*
****************************************************************************/
FAR char *mfs_ser_timespec(FAR const struct timespec * const x,
FAR char * const out);
/****************************************************************************
* Name: mfs_deser_timespec
*
* Description:
* Deserialize timespec.
*
* Input Parameters:
* in - Input array from where to deserialize.
* x - Value to deserialize
*
* Returned Value:
* Pointer to byte after the end of serialized value.
*
****************************************************************************/
FAR const char *mfs_deser_timespec(FAR const char * const in,
FAR struct timespec * const x);
/****************************************************************************
* Name: mfs_ser_16
*
* Description:
* Serialize 16 bit values.
*
* Input Parameters:
* x - Value to serialize
* out - Output array where to serialize.
*
* Returned Value:
* Pointer to byte after the end of serialized value.
*
****************************************************************************/
FAR char *mfs_ser_16(const uint16_t n, FAR char * const out);
/****************************************************************************
* Name: mfs_deser_16
*
* Description:
* Deserialize 16 bit value.
*
* Input Parameters:
* in - Input array from where to deserialize.
* x - Value to deserialize
*
* Returned Value:
* Pointer to byte after the end of serialized value.
*
****************************************************************************/
FAR const char *mfs_deser_16(FAR const char * const in, FAR uint16_t *n);
/****************************************************************************
* Name: mfs_v2n
*
* Description:
* v2n(n) math function.
*
* Input Parameters:
* n - Number.
*
* Returned Value:
* v2n(n).
*
****************************************************************************/
mfs_t mfs_v2n(mfs_t n);
/****************************************************************************
* Name: mfs_set_msb
*
* Description:
* The mosr significant set bit location.
*
* Input Parameters:
* n - Number.
*
* Returned Value:
* Number after setting the bit.
*
****************************************************************************/
mfs_t mfs_set_msb(mfs_t n);
bool mfs_ctz_eq(FAR const struct mfs_ctz_s * const a,
FAR const struct mfs_ctz_s * const b);
bool mfs_path_eq(FAR const struct mfs_path_s * const a,
FAR const struct mfs_path_s * const b);
/* mnemofs_ctz.c */
/****************************************************************************
* Name: mfs_ctz_rdfromoff_new
*
* Description:
* Read from a specific offset into the data in a CTZ file. New version.
*
* Input Parameters:
* sb - Superblock instance of the device.
* ctz - CTZ list.
* data_off - Offset into the data.
* len - Length of the buffer.
* buf - Buffer to store read contents.
*
* Assumptions/Limitations:
* The CTZ list provided should be the updated location from the journal.
*
****************************************************************************/
int mfs_ctz_rdfromoff(FAR const struct mfs_sb_s * const sb,
const struct mfs_ctz_s ctz, mfs_t data_off,
mfs_t len, FAR char * buf);
/****************************************************************************
* Name: mfs_ctz_wrtnode
*
* Description:
* Write an LRU node to the flash. It also adds a log of it to the journal.
*
* Input Parameters:
* sb - Superblock instance of the device.
* node - LRU node
*
* Assumptions/Limitations:
* - Assumes path is updated by journal. This will also write corresponding
* journal log.
*
* - This is the most computationally heavy part of the entire file system
* from a human POV, and one of the most for MCU or computer (as init
* methods are heavier).
*
****************************************************************************/
int mfs_ctz_wrtnode(FAR struct mfs_sb_s * const sb,
FAR const struct mfs_node_s * const node,
FAR struct mfs_ctz_s *new_loc);
/****************************************************************************
* Name: mfs_ctz_travel
*
* Description:
* From CTZ block at page `pg_src` and index `idx_src`, give the page
* number of index `idx_dest`.
*
* The source is preferably the last CTZ block in the CTZ list, but it can
* realistically be any CTZ block in the CTZ list whos position is known.
* However, `idx_dest <= idx_src` has to be followed. Takes O(log(n))
* complexity to travel.
*
* Input Parameters:
* sb - Superblock instance of the device.
* idx_src - Index of the source ctz block.
* pg_src - Page number of the source ctz block.
* idx_dest - Index of the destination ctz block.
*
* Returned Value:
* The page number corresponding to `idx_dest`.
*
* Assumptions/Limitations:
* `idx_dest <= idx_src`.
*
****************************************************************************/
mfs_t mfs_ctz_travel(FAR const struct mfs_sb_s * const sb,
mfs_t idx_src, mfs_t pg_src, mfs_t idx_dest);
/* mnemofs_lru.c */
/****************************************************************************
* Name: mfs_lru_ctzflush
*
* Description:
* Flush the updates of a CTZ list inside the LRU to the flash.
*
* Input Parameters:
* sb - Superblock instance of the device.
* path - CTZ representation of the relpath.
* depth - Depth of path.
*
****************************************************************************/
int mfs_lru_ctzflush(FAR struct mfs_sb_s * const sb,
FAR struct mfs_path_s * const path, const mfs_t depth);
/****************************************************************************
* Name: mfs_lru_del
*
* Description:
* Delete instruction to LRU.
*
* Input Parameters:
* sb - Superblock instance of the device.
* off - Offset into the data.
* bytes - Number of bytes to delete.
* path - CTZ representation of the relpath.
* depth - Depth of path.
*
****************************************************************************/
int mfs_lru_del(FAR struct mfs_sb_s * const sb, const mfs_t data_off,
mfs_t bytes, FAR struct mfs_path_s * const path,
const mfs_t depth);
/****************************************************************************
* Name: mfs_lru_wr
*
* Description:
* Write to LRU
*
* Input Parameters:
* sb - Superblock instance of the device.
* data_off - Offset into the data.
* bytes - Number of bytes to delete.
* path - CTZ representation of the relpath.
* depth - Depth of path.
* buf - Buffer.
*
****************************************************************************/
int mfs_lru_wr(FAR struct mfs_sb_s * const sb, const mfs_t data_off,
mfs_t bytes, FAR struct mfs_path_s * const path,
const mfs_t depth, FAR const char *buf);
/****************************************************************************
* Name: mfs_lru_init
*
* Description:
* Initialize LRU.
*
* Input Parameters:
* sb - Superblock instance of the device.
*
****************************************************************************/
void mfs_lru_init(FAR struct mfs_sb_s * const sb);
/****************************************************************************
* Name: mfs_lru_rdfromoff
*
* Description:
* Read updated data from an offset upto a certain number of bytes.
*
* Input Parameters:
* sb - Superblock instance of the device.
* data_off - Offset into the data.
* path - CTZ representation of the relpath.
* depth - Depth of path.
* buf - Buffer to populate with data.
* buflen - Length of data to read.
*
****************************************************************************/
int mfs_lru_rdfromoff(FAR const struct mfs_sb_s * const sb,
const mfs_t data_off,
FAR struct mfs_path_s * const path, const mfs_t depth,
FAR char *buf, const mfs_t buflen);
/****************************************************************************
* Name: mfs_lru_updatedinfo
*
* Description:
* Update information of the path.
*
* Input Parameters:
* sb - Superblock instance of the device.
* path - CTZ representation of the relpath.
* depth - Depth of path.
*
* Assumptions/Limitations:
* The will update path.
*
****************************************************************************/
int mfs_lru_updatedinfo(FAR const struct mfs_sb_s * const sb,
FAR struct mfs_path_s * const path,
const mfs_t depth);
/****************************************************************************
* Name: mfs_lru_updatectz
*
* Description:
* Update CTZ location of an fs object.
*
* Input Parameters:
* sb - Superblock instance of the device.
* path - Old CTZ representation of the relpath.
* depth - Depth of path.
* new_ctz - New CTZ location.
*
* Assumptions/Limitations:
* The update the CTZ portion of the direntry in the parent.
* This updates the path as well.
*
****************************************************************************/
int mfs_lru_updatectz(FAR struct mfs_sb_s * sb,
FAR struct mfs_path_s * const path, const mfs_t depth,
const struct mfs_ctz_s new_ctz, mfs_t new_sz);
bool mfs_lru_isempty(FAR struct mfs_sb_s * const sb);
int mfs_lru_flush(FAR struct mfs_sb_s * const sb);
/* mnemofs_master.c */
/****************************************************************************
* Name: mfs_mn_init
*
* Description:
* Initialize master node by reading from the flash.
*
* Input Parameters:
* sb - Superblock instance of the device.
* jrnl_blk - First block of the journal.
*
* Returned Value:
* 0 - OK
* < 0 - Error
*
* Assumptions/Limitations:
* The journal will have to be initialized before this.
*
****************************************************************************/
int mfs_mn_init(FAR struct mfs_sb_s * const sb, const mfs_t jrnl_blk);
/****************************************************************************
* Name: mfs_mn_fmt
*
* Description:
* Format a master node.
*
* Input Parameters:
* sb - Superblock instance of the device.
* jrnl_blk - First block of the journal.
*
* Returned Value:
* 0 - OK
* < 0 - Error
*
* Assumptions/Limitations:
* The journal will have to be formatted before this.
*
****************************************************************************/
int mfs_mn_fmt(FAR struct mfs_sb_s * const sb, const mfs_t blk1,
const mfs_t blk2, const mfs_t jrnl_blk);
/****************************************************************************
* Name: mfs_mn_move
*
* Description:
* Move the master node.
*
* Input Parameters:
* sb - Superblock instance of the device.
* root - New location of the root of the file system.
* root_sz - New size of the CTZ list of the root of the file syste.
*
* Returned Value:
* 0 - OK
* < 0 - Error
*
* Assumptions/Limitations:
* This is called when the root is updated to a new location.
*
****************************************************************************/
int mfs_mn_move(FAR struct mfs_sb_s * const sb, struct mfs_ctz_s root,
const mfs_t root_sz);
int mfs_mn_sync(FAR struct mfs_sb_s *sb,
FAR struct mfs_path_s * const new_loc,
const mfs_t blk1, const mfs_t blk2, const mfs_t jrnl_blk);
/* mnemofs_fsobj.c */
/****************************************************************************
* Name: mfs_path2childname
*
* Description:
* Given a path, point to the start of the name of the last fs object in
* that path (child).
*
* Input Parameters:
* relpath - Path.
*
* Returned Value:
* The pointer pointing to where the name of the child starts.
*
* Assumptions/Limitations:
* This does not allocate a new array for the name, but rather just points
* to the place in `relpath` where the name starts.
*
****************************************************************************/
FAR const char * mfs_path2childname(FAR const char *relpath);
/****************************************************************************
* Name: mfs_get_fsz
*
* Description:
* Get updated file size of a file.
*
* Input Parameters:
* sb - Superblock instance of the device.
* path - CTZ representation of the path.
* depth - Depth of the path.
*
* Returned Value:
* The size of the file.
*
****************************************************************************/
mfs_t mfs_get_fsz(FAR struct mfs_sb_s * const sb,
FAR const struct mfs_path_s * const path,
const mfs_t depth);
/****************************************************************************
* Name: mfs_get_patharr
*
* Description:
* Takes a relpath, and returns the CTZ location of every file system
* object that occurs in the relpath. This also returns a bit flag
* consisting of items from `MFS_PATH_FLAGS`.
*
* Input Parameters:
* sb - Superblock instance of the device.
* relpath - Path of the file.
* path - To populate with CTZ representation of the path.
* depth - To populate with depth of the path.
*
* Returned Value:
* 0 - OK
* < - Error
*
* Assumptions/Limitations:
* This allocates the `path` array in heap, and transfers the ownership
* of this array to the caller. It's the caller's reponsibility to use this
* with `mfs_free_patharr`.
*
****************************************************************************/
int mfs_get_patharr(FAR const struct mfs_sb_s * const sb,
FAR const char * relpath, FAR struct mfs_path_s **path,
FAR mfs_t *depth);
/****************************************************************************
* Name: mfs_free_patharr
*
* Description:
* Frees up a CTZ representation of the relpath.
*
* Input Parameters:
* path - CTZ representation of the relpath.
*
****************************************************************************/
void mfs_free_patharr(FAR struct mfs_path_s *path);
/****************************************************************************
* Name: mfs_obj_isempty
*
* Description:
* Checks if a fs object is empty by reading its directory entry.
*
* Input Parameters:
* sb - Superblock instance of the device.
* pitr - Parent iterator pointing to the fs object.
*
* Returned Value:
* Is the file or directory pointer by the direntry empty.
*
* Assumptions/Limitations:
* The `pitr` should point to the fs object being checked.
*
****************************************************************************/
bool mfs_obj_isempty(FAR struct mfs_sb_s * const sb,
FAR struct mfs_path_s *path,
FAR struct mfs_pitr_s * const pitr);
/****************************************************************************
* Name: mfs_pitr_init
*
* Description:
* Initialize a parent iterator (pitr). This iterator starts at the start
* of the parent's directory file, and is used to iterate over the
* directory entries (direntries) the file has.
*
* Input Parameters:
* sb - Superblock instance of the device.
* path - CTZ representation of the relpath.
* depth - Depth of CTZ representation of the path.
* pitr - To initialize Parent iterator.
* child - If the child exists in the path.
*
* Assumptions/Limitations:
* - The `pitr` should point to the fs object being checked.
*
* - If the `child` is set to true, it assumes the child is present in the
* directory file. If not, it assumes it's not present in the path (due
* to whatever circumstances like it's not known if the child exists in
* the parent or not, search in parent file, etc.).
*
* - This assumes the pitr is initialized and freed in the same locked
* context. If, at all, this is not the case, then at the start of every
* new locked context till it's freed, this needs to be synced.
*
* - This also assumes that a CTZ will not change location before the pitr
* is destroyed. If at all a CTZ changes location, the pitr needs to be
* updated.
*
* - This contains a pitr with all fields set to 0 when it's invalid.
*
****************************************************************************/
int mfs_pitr_init(FAR const struct mfs_sb_s * const sb,
FAR const struct mfs_path_s * const path,
const mfs_t depth, FAR struct mfs_pitr_s * const pitr,
bool child);
/****************************************************************************
* Name: mfs_pitr_free
*
* Description:
* Free an initialized pitr.
*
* Input Parameters:
* pitr - Parent iterator.
*
* Assumptions/Limitations:
* It's mainly for symbolic purpose, and the only thing it does is make all
* the members reset to 0.
*
****************************************************************************/
void mfs_pitr_free(FAR const struct mfs_pitr_s * const pitr);
/****************************************************************************
* Name: mfs_pitr_adv
*
* Description:
* Advance a pitr to the next direntry.
*
* Input Parameters:
* sb - Superblock instance of the device.
* pitr - Parent iterator.
*
* Assumptions/Limitations:
* This is best used if we have not already read the value of a dirent. If
* we have, then we do it the `mfs_pitr_adv_dirent` way.
*
****************************************************************************/
int mfs_pitr_adv(FAR struct mfs_sb_s * const sb,
FAR struct mfs_path_s *path,
FAR struct mfs_pitr_s * const pitr);
/****************************************************************************
* Name: mfs_pitr_adv_bydirent
*
* Description:
* Advance a pitr to the next direntry by using current direntry.
*
* Input Parameters:
* pitr - Parent iterator.
* dirent - Current directory entry.
*
* Assumptions/Limitations:
* This is best used if we have already got the dirent by which we want to
* advance entry (the one pointed by pitr). If we don't, then we do it the
* `mfs_pitr_adv` way.
*
****************************************************************************/
void mfs_pitr_adv_bydirent(FAR struct mfs_pitr_s * const pitr,
FAR const struct mfs_dirent_s * const dirent);
/****************************************************************************
* Name: mfs_pitr_adv_off
*
* Description:
* Advance a pitr by an offset.
*
* Input Parameters:
* pitr - Parent iterator.
*
* Assumptions/Limitations:
* This is best used if we have already got the offset by which we want to
* advance entry. This assumes that once the offset is applied, the new
* pitr location is valid, and start of a direntry.
*
****************************************************************************/
void mfs_pitr_adv_off(FAR struct mfs_pitr_s * const pitr,
const mfs_t off);
/****************************************************************************
* Name: mfs_pitr_adv_tochild
*
* Description:
* Advance the pitr to point to the child mentioned in the path.
*
* Input Parameters:
* pitr - Parent iterator.
* path - CTZ representation of the relpath
*
* Assumptions/Limitations:
* This assumes that the pitr is initialized for the immediate parent of the
* child.
*
****************************************************************************/
void mfs_pitr_adv_tochild(FAR struct mfs_pitr_s * const pitr,
FAR const struct mfs_path_s * const path);
/****************************************************************************
* Name: mfs_pitr_reset
*
* Description:
* Reset a pitr to point to the start of the CTZ file.
*
* Input Parameters:
* pitr - Parent iterator.
*
****************************************************************************/
void mfs_pitr_reset(FAR struct mfs_pitr_s * const pitr);
/****************************************************************************
* Name: mfs_pitr_readdirent
*
* Description:
* Reads the direntry pointed to by the pitr.
*
* Input Parameters:
* sb - Superblock instance of the device.
* pitr - Parent Iterator.
* dirent - Direntry to populate.
*
* Returned Value:
* Length of the name of the FS object in the direntry.
*
* Assumptions/Limitations:
* This alloctes `dirent`, and transfers the ownership to the caller. It's
* the caller's responsibility to get it freed using mfs_free_dirent.
*
****************************************************************************/
int mfs_pitr_readdirent(FAR const struct mfs_sb_s * const sb,
FAR struct mfs_path_s *path,
FAR struct mfs_pitr_s * const pitr,
FAR struct mfs_dirent_s **dirent);
/****************************************************************************
* Name: mfs_pitr_readdirent
*
* Description:
* Free an allocated direntry.
*
* Input Parameters:
* dirent - Direntry.
*
****************************************************************************/
void mfs_free_dirent(FAR struct mfs_dirent_s *dirent);
/****************************************************************************
* Name: mfs_searchfopen
*
* Description:
* Checks if a file is already open.
*
* In mnemofs, this is done by iterating through the kernel list of open
* files.
*
* Input Parameters:
* sb - Superblock instance of the device.
* path - CTZ representation of the relpath.
* depth - Depth of path.
*
* Returned Value:
* True - File is already open.
* False - File is not open.
*
****************************************************************************/
bool mfs_searchfopen(FAR const struct mfs_sb_s * const sb,
FAR const struct mfs_path_s * const path,
const mfs_t depth);
/****************************************************************************
* Name: mfs_pitr_appenddirent
*
* Description:
* Appeend a direntry at the end of the directory file of parent that's
* initialized in pitr.
*
* In mnemofs, this is done by iterating through the kernel list of open
* files.
*
* Input Parameters:
* sb - Superblock instance of the device.
* path - CTZ representation of the relpath.
* depth - Depth of path.
* pitr - Parent Iterator.
* dirent - Directory entry to be added.
*
* Returned Value:
* 0 - OK
* < 0 - Error
*
****************************************************************************/
int mfs_pitr_appenddirent(FAR struct mfs_sb_s * const sb,
FAR struct mfs_path_s * const path,
const mfs_t depth,
FAR struct mfs_pitr_s * const pitr,
FAR const struct mfs_dirent_s * const dirent);
/****************************************************************************
* Name: mfs_pitr_appendnew
*
* Description:
* Appeend a new entry at the end of the directory file of parent that's
* initialized in pitr. This creates the direntry.
*
* Input Parameters:
* sb - Superblock instance of the device.
* path - CTZ representation of the relpath.
* depth - Depth of path.
* pitr - Parent Iterator.
* relpath - Relative path of the fs object.
* mode - Mode of the file/directory.
*
* Returned Value:
* 0 - OK
* < 0 - Error
*
* Assumptions/Limitations:
* This assumes that the fs object desired to be appended appears at the
* end of the entire relpath.
*
****************************************************************************/
int mfs_pitr_appendnew(FAR struct mfs_sb_s * const sb,
FAR struct mfs_path_s * const path, const mfs_t depth,
FAR struct mfs_pitr_s * const pitr,
FAR const char * const relpath, const mode_t mode);
/****************************************************************************
* Name: mfs_pitr_rmdirent
*
* Description:
* Removes the dirent from its parent's directory file.
*
* Input Parameters:
* sb - Superblock instance of the device.
* path - CTZ representation of the relpath.
* depth - Depth of path.
* pitr - Parent Iterator.
* dirent - Directory entry to be added.
*
* Returned Value:
* 0 - OK
* < 0 - Error
*
****************************************************************************/
int mfs_pitr_rmdirent(FAR struct mfs_sb_s * const sb,
FAR struct mfs_path_s * const path,
const mfs_t depth, FAR struct mfs_pitr_s * const pitr,
FAR const struct mfs_dirent_s * const dirent);
/****************************************************************************
* Name: mfs_pitr_rm
*
* Description:
* Removes the fs object pointed by path from its parent's directory file.
*
* Input Parameters:
* sb - Superblock instance of the device.
* path - CTZ representation of the relpath.
* depth - Depth of path.
*
* Returned Value:
* 0 - OK
* < 0 - Error
*
* Assumptions/Limitations:
* This does not require any form of pitr initialization. It does it inside
* itself.
*
****************************************************************************/
int mfs_pitr_rm(FAR struct mfs_sb_s * const sb,
FAR struct mfs_path_s * const path,
const mfs_t depth, bool rm_child);
int mfs_pitr_traversefs(FAR struct mfs_sb_s * sb, const struct mfs_ctz_s ctz,
int type);
#undef EXTERN
#ifdef __cplusplus
}
#endif
#endif /* __FS_MNEMOFS_MNEMOFS_H */