Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 637af313 authored by Sharvil Nanavati's avatar Sharvil Nanavati Committed by Andre Eisenbach
Browse files

Start putting together a style guide for bluedroid.

It's far from complete but provides a starting point for codifying
best practices and guidelines for developing in this codebase.
parent 7ac3efff
Loading
Loading
Loading
Loading
+174 −0
Original line number Diff line number Diff line
# Bluedroid Style Guide
This document outlines the coding conventions and code style used in Bluedroid.
Its primary purpose is to provide explicit guidance on style so that developers
are consistent with one another and spend less time debating style.

## Directory structure
Directories at the top-level should consist of major subsystems in Bluedroid.
Each subsystem's purpose should be documented in the `doc/directory_layout.md`
file, even if it seems obvious from the name.

For a subsystem that contains code, its directory structure should look like:
```
  Android.mk
  include/
  src/
  test/
```
Further, the directory structure inside `src/` and `include/` should be
mirrored. In other words, if `src/` contains a subdirectory called `foo/`,
`include/` must also have a subdirectory named `foo/`.

## Target architecture
Bluedroid targets a variety of hardware and cannot make many assumptions about
memory layout, sizes, byte order, etc. As a result, some operations are
considered unsafe and this section outlines the most important ones to watch out
for.

### Pointer / integer casts
In general, do not cast pointers to integers or vice versa.

The one exception is if an integer needs to be temporarily converted to a
pointer and then back to the original integer. This style of code is typically
needed when providing an integral value as the context to a callback, as in the
following example.
```
void my_callback(void *context) {
  uintptr_t arg = context;
}

set_up_callback(my_callback, (uintptr_t)5);
```
Note, however, that the integral value was written into the pointer and read
from the pointer as a `uintptr_t` to avoid a loss of precision (or to make the
loss explicit).

### Byte order
It is not safe to assume any particular byte order. When serializing or
deserializing data, it is unsafe to memcpy unless both source and destination
pointers have the same type.

## Language
Bluedroid is written in C99 and should take advantage of the features offered by
it. However, not all language features lend themselves well to the type of
development required by Bluedroid. This section provides guidance on some of the
features to embrace or avoid.

### C Preprocessor
The use of the C preprocessor should be minimized. In particular:
* use functions or, if absolutely necessary, inline functions instead of macros
* use `static const` variables instead of `#define`
* use `enum` for enumerations, not a collection of `#define`s
* minimize the use of feature / conditional macros

The last point is perhaps the most contentious. It's well-understood that
feature macros are useful in reducing code size but it leads to an exponential
explosion in build configurations. Setting up, testing, and verifying each of
the `2^n` build configurations is untenable for `n` greater than, say, 4.

### C++
Although C++ offers constructs that may make Bluedroid development faster,
safer, more pleasant, etc. the decision _for the time being_ is to stick with
pure C99. The exceptions are when linking against libraries that are written
in C++. At the time of writing these libraries are `gtest` and `tinyxml2`,
where the latter is a dependency that should be eliminated in favor of simpler,
non-XML formats.

### Variadic functions
Variadic functions are dangerous and should be avoided for most code. The
exception is when implementing logging since the benefits of readability
outweigh the cost of safety.

### Zero-length arrays
Use zero-length arrays as the last member of a struct if the array needs to be
allocated in contiguous memory with its containing struct. For example:
```
typedef struct {
  size_t length;
  uint8_t data[0];
} buffer_t;

// Allocate a buffer with 128 bytes available for my_buffer->data.
buffer_t *my_buffer = malloc(sizeof(buffer_t) + 128);
uint8_t *data = my_buffer->data;
```

### Pointer arithmetic
Avoid pointer arithmetic when possible as it results in difficult to read code.
Prefer array-indexing syntax over pointer arithmetic.

In particular, do not write code like this:
```
typedef struct {
  size_t length;
} buffer_t;

buffer_t *my_buffer = malloc(sizeof(buffer_t) + 128);
uint8_t *data = (uint8_t *)(my_buffer + 1);
```
Instead, use zero-length arrays as described above to avoid pointer arithmetic
and array indexing entirely.

## Header files
In general, every source file (`.c` or `.cpp`) in a `src/` directory should
have a corresponding header (`.h`) in the `include/` directory.

### Template header file
```
[copyright header]

#pragma once

#include <system/a.h>
#include <system/b.h>

#include "subsystem/include/a.h"
#include "subsystem/include/b.h"

typedef struct alarm_t alarm_t;
typedef struct list_t list_t;

// This comment describes the following function. It is not a structured
// comment, it's English prose. Function arguments can be referred to as
// |param|. This function returns true if a new object was created, false
// otherwise.
bool template_new(const list_t *param);

// Each public function must have a comment describing its semantics. In
// particular, edge cases, and whether a pointer argument may or may not be
// NULL.
void template_use_alarm(alarm_t *alarm);
```

### License header
Each header file must begin with the following Apache 2.0 License with `<year>`
and `<owner>` replaced with the year in which the file was authored and the
owner of the copyright, respectively.
```
/******************************************************************************
 *
 *  Copyright (C) <year> <owner>
 *
 *  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.
 *
 ******************************************************************************/
```

### Include guard
After the license header, each header file must contain the include guard:
```
#pragma once
```
This form is used over traditional `#define`-based include guards as it is less
error-prone, doesn't pollute the global namespace, is more compact, and can
result in faster compilation.