In addition to the previous instructions, please check PRs for style.
- maximum text width is 80 characters
- indentation is 4 spaces
- no tabs, no trailing spaces
- list elements on the same line are separated with spaces
- hexadecimal literals are lowercase
- file names, function and type names, and global variables have the
njs_prefix
size_t
njs_utf8_length(u_char *p, size_t n)
{
u_char c, *last;
size_t len;
last = p + n;
for (len = 0; p < last; len++) {
c = *p;
if (c < 0x80) {
p++;
continue;
}
if (njs_utf8_decode(&p, last - p) > 0x10ffff) {
/* invalid UTF-8 */
return n;
}
}
return len;
}
A typical source file may contain the following sections separated by two empty lines:
- copyright statements
- includes
- preprocessor definitions
- type definitions
- function prototypes
- variable definitions
- function definitions
Copyright statements look like this:
/*
* Copyright (C) Author Name
* Copyright (C) Organization, Inc.
*/
If the file is modified significantly, the list of authors should be updated, the new author is added to the top.
The njs_main.h file is always included first. Then follow optional external header files:
#include <njs_main.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxslt/xslt.h>
#if (NJS_HAVE_EXSLT)
#include <libexslt/exslt.h>
#endifHeader files should include the so called "header protection":
#ifndef _NJS_VM_H_INCLUDED_
#define _NJS_VM_H_INCLUDED_
...
#endif /* _NJS_VM_H_INCLUDED_ */- "//" comments are not used
- text is written in English, American spelling is preferred
- multi-line comments are formatted like this:
/*
* The red-black tree code is based on the algorithm described in
* the "Introduction to Algorithms" by Cormen, Leiserson and Rivest.
*/
/* find the server configuration for the address:port */Macro names start from njs_ or NJS_ prefix. Macro names for constants are uppercase. Parameterized macros and macros for initializers are lowercase. The macro name and value are separated by at least two spaces:
#define NJS_STRING_MAX_LENGTH 0x7fffffff
#define njs_queue_is_empty(queue) ((queue) == (queue)->prev)
#define njs_queue_insert_head(queue, link) \
(link)->next = (queue)->next; \
(link)->next->prev = (link); \
(link)->prev = (queue); \
(queue)->next = (link)
#define njs_null_string { 0, NULL }Conditions are inside parentheses, negation is outside:
#if (NJS_HAVE_OPENSSL)
...
#elif (NJS_HAVE_QUICKJS)
...
#elif (NJS_HAVE_ADDR2LINE)
...
#else /* default */
...
#endif /* NJS_HAVE_OPENSSL */Type names end with the _t suffix. A defined type name is separated by at least two spaces:
typedef uint8_t njs_vmcode_t;Structure types are defined using typedef. Inside structures, member types and names are aligned:
typedef struct {
size_t length;
u_char *start;
} njs_str_t;Keep alignment identical among different structures in the file. A structure that points to itself has the name, ending with _s. Adjacent structure definitions are separated with two empty lines:
typedef struct njs_queue_link_s njs_queue_link_t;
struct njs_queue_link_s {
njs_queue_link_t *prev;
njs_queue_link_t *next;
};
typedef struct {
njs_queue_link_t head;
} njs_queue_t;Each structure member is declared on its own line. The asterisk (*) in pointer declarations is aligned with the field name, not the type:
typedef struct {
u_char *start;
u_char *end;
njs_str_t file;
njs_str_t name;
njs_arr_t *lines; /* of njs_vm_line_num_t */
} njs_vm_code_t;Function pointers inside structures have defined types ending with _pt:
typedef ssize_t (*ngx_recv_pt)(ngx_connection_t *c, u_char *buf, size_t size);
typedef ssize_t (*ngx_recv_chain_pt)(ngx_connection_t *c, ngx_chain_t *in,
off_t limit);
typedef ssize_t (*ngx_send_pt)(ngx_connection_t *c, u_char *buf, size_t size);
typedef ngx_chain_t *(*ngx_send_chain_pt)(ngx_connection_t *c, ngx_chain_t *in,
off_t limit);
typedef struct {
ngx_recv_pt recv;
ngx_recv_chain_pt recv_chain;
ngx_recv_pt udp_recv;
ngx_send_pt send;
ngx_send_pt udp_send;
ngx_send_chain_pt udp_send_chain;
ngx_send_chain_pt send_chain;
ngx_uint_t flags;
} ngx_os_io_t;Enumerations have types ending with _t:
typedef enum {
NJS_NULL,
NJS_UNDEFINED,
NJS_BOOLEAN,
NJS_NUMBER,
NJS_STRING,
NJS_SYMBOL
} njs_value_type_t;Variables are declared sorted by length of a base type, then alphabetically. Type names and variable names are aligned. The type and name "columns" are separated with two spaces. Large arrays are put at the end of a declaration block:
u_char *rv, *p;
njs_vm_t *vm;
njs_uint_t i, j, k;
njs_parser_t *parser;
njs_value_t *values;
unsigned int len;
njs_generator_t *generator;
struct sockaddr *sa;
const unsigned char *data;
u_char text[NJS_STRING_MAX_LENGTH];Static and global variables may be initialized on declaration:
static njs_str_t njs_string_constructor = njs_str("String");
static njs_uint_t mday[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static uint32_t njs_crc32_table16[] = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
...
0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
};There is a bunch of commonly used type/name combinations:
u_char *p, *start, *end;
njs_int_t ret;
njs_vm_t *vm;
njs_value_t *value;
njs_parser_t *parser;
njs_function_t *function;
njs_generator_t *generator;All functions (even static ones) should have prototypes. Prototypes include argument names. Long prototypes are wrapped with a single indentation on continuation lines:
static njs_int_t njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end);
static njs_int_t njs_parser_statement(njs_parser_t *parser,
njs_parser_node_t *parent);
static njs_int_t njs_generator_function(njs_vm_t *vm,
njs_generator_t *generator, njs_parser_node_t *node);The function name in a definition starts with a new line. The function body opening and closing braces are on separate lines. The body of a function is indented. There are two empty lines between functions:
static njs_int_t
njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end)
{
...
}
static njs_int_t
njs_parser_statement(njs_parser_t *parser, njs_parser_node_t *parent,
njs_token_type_t token)
{
...
}There is no space after the function name and opening parenthesis. Long function calls are wrapped such that continuation lines start from the position of the first function argument. If this is impossible, format the first continuation line such that it ends at position 79:
njs_printf(vm, "parser: %V at %uz",
&token->text, parser->lexer->start);
value = njs_mp_alloc(vm->mem_pool,
parser->scope->items * sizeof(njs_value_t));The njs_inline macro should be used instead of inline:
static njs_inline njs_int_t njs_utf8_byte(const u_char ***start, const u_char *end);Binary operators except "." and "−>" should be separated from their operands by one space. Unary operators and subscripts are not separated from their operands by spaces:
width = width * 10 + (*fmt++ - '0');
ch = (u_char) ((decoded << 4) + (ch - '0'));
value->data.u.string = &parser->lexer->text.start[i + 1];Type casts are separated by one space from casted expressions. An asterisk inside type cast is separated with space from type name:
len = njs_utf8_decode((const u_char **) &start, end);If an expression does not fit into single line, it is wrapped. The preferred point to break a line is a binary operator. The continuation line is lined up with the start of expression:
if (type == NJS_NULL
|| type == NJS_UNDEFINED
|| type == NJS_BOOLEAN
|| type == NJS_NUMBER
|| type == NJS_STRING)
{
...
}
parser->error = "unexpected token "
"in expression";As a last resort, it is possible to wrap an expression so that the continuation line ends at position 79:
values = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t)
+ count * sizeof(njs_object_prop_t));The above rules also apply to sub-expressions, where each sub-expression has its own indentation level:
if (((parser->scope->type & NJS_SCOPE_FUNCTION)
|| generator->context)
&& !vm->options.backtrace
&& parser->scope->nesting_level > 1)
{
...
}Sometimes, it is convenient to wrap an expression after a cast. In this case, the continuation line is indented:
value = (njs_value_t *)
((u_char *) prop - offsetof(njs_object_prop_t, value));Pointers are explicitly compared to NULL (not 0):
if (ptr != NULL) {
...
}The if keyword is separated from the condition by one space. Opening brace is located on the same line, or on a dedicated line if the condition takes several lines. Closing brace is located on a dedicated line, optionally followed by else if / else. Usually, there is an empty line before the else if / else part:
if (value->type == NJS_NULL) {
result = njs_value_undefined;
goto done;
} else if (value->type == NJS_UNDEFINED) {
result = njs_value_null;
goto done;
} else {
result = njs_value_to_string(vm, value);
if (result == NULL) {
goto error;
} else {
goto done;
}
}Similar formatting rules are applied to do and while loops:
while (p < end && *p == ' ') {
p++;
}
do {
parser->node = current;
parser = parser->parent;
} while (parser);The switch keyword is separated from the condition by one space. Opening brace is located on the same line. Closing brace is located on a dedicated line. The case keywords are lined up with switch:
switch (token) {
case NJS_TOKEN_FUNCTION:
parser->state = njs_parser_function;
break;
case NJS_TOKEN_VAR:
parser->state = njs_parser_var;
break;
default:
parser->state = njs_parser_statement;
break;
}Most for loops are formatted like this:
for (i = 0; i < parser->scope->items; i++) {
...
}
for (prop = njs_object_prop(object);
prop < njs_object_prop_end(object);
prop = njs_object_prop_next(prop))
{
...
}If some part of the for statement is omitted, this is indicated by the /* void */ comment:
for (i = 0; /* void */ ; i++) {
...
}A loop with an empty body is also indicated by the /* void */ comment which may be put on the same line:
for (prop = object->start; prop->next; prop = prop->next) { /* void */ }An endless loop looks like this:
for ( ;; ) {
...
}Labels are surrounded with empty lines and are indented at the previous level:
if (i == 0) {
vm->error = "function not found";
goto failed;
}
values = njs_mp_alloc(vm->mem_pool, i * sizeof(njs_value_t));
if (values == NULL) {
goto failed;
}
parser->values = i;
...
return NJS_OK;
failed:
njs_mp_free(vm->mem_pool, values);
return NJS_ERROR;