﻿# Exploring Nau Engine codebase: pt\.1

This article begins a trilogy about the Nau Engine game engine\. In the first part, we focus on its functionality and explore three categories of common errors: copy\-paste errors, logic flaws, and memory issues\.

![1221_NauEngineOne/image1.png](https://import.viva64.com/docx/blog/1221_NauEngineOne/image1.png)

## What is Nau Engine?

[Nau Engine](https://nauengine.org/) is a game engine designed to streamline the development and maintenance of games\. Its developers say that their tool should be suitable for any task, any developer, and any platform\.

1\. **For any task**: Nau Engine has tools for every stage of game development, from initial creation to post\-release support\. This includes systems for analytics, error handling, and content updates\.

2\. **For any developer**: The engine is good for developers with different levels of experience\. It provides intuitive tools and templates to help beginners easily get started, along with deeper customization options for advanced users\.

3\. **For any platform**: The engine allows seamless adaptation across various platforms, including PC, mobile, and web, making development more flexible and convenient\.

A standout feature of Nau Engine is its adapted **ECS \(Entity Component System\) library** from the Dagor engine\. This library not only delivers high performance but also brings back memories of classic games\.

Overall, Nau Engine aims to help developers at all stages of game development, equipping them with the necessary tools and facilities to bring their ideas to life\.

The following analysis is based on the repository code from the [020cbfe](https://github.com/NauEngine/NauEnginePublic/tree/020cbfec7bd4cff7ff4303e0f31d75eebed584be) commit\.

## Analysis results

### Copy\-paste errors

**Fragment N1**

```cpp
NAU_ASSERT(irRequest.usage.access == req.usage.access 
        && irRequest.usage.type == irRequest.usage.type);
```

The PVS\-Studio warning: [_V501_](https://pvs-studio.com/en/docs/warnings/v501/)_ There are identical sub\-expressions 'irRequest\.usage\.type' to the left and to the right of the '\=\=' operator\. _[_irGraphBuilder\.cpp 719_](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/graphics/src/daBfg/frontend/irGraphBuilder.cpp#L719)

Let's look at the `NAU_ASSERT` macro, which checks conditions during code execution\. Specifically, take a closer peek at the second comparison within this macro, `irRequest.usage.type == irRequest.usage.type`\. If we write them separately, we notice that developers use the identical expression on the left and right sides of the comparison\. As a result, the condition will always be true\.

Most likely, developers have intended to compare `irRequest.usage.type` with `req.usage.type` to check if the types in two different objects are similar:

```cpp
NAU_ASSERT(irRequest.usage.access == req.usage.access 
        && irRequest.usage.type   == req.usage.type);
```

**Fragment N2**

```cpp
bool VFXModFXInstance::deserialize(const nau::DataBlock* blk)
{
  ....
  m_life.part_life_min = blk->getReal("lifeMin", 5.0f);
  m_life.part_life_max = blk->getReal("lifeMin", 5.0f);
  ....
}
```

The PVS\-Studio warning: [_V656_](https://pvs-studio.com/en/docs/warnings/v656/)_ Variables 'm\_life\.part\_life\_min', 'm\_life\.part\_life\_max' are initialized through the call to the same function\. It's probably an error or un\-optimized code\. Consider inspecting the 'blk\-\>getReal\("lifeMin", 5\.0f\)' expression\. Check lines: 94, 95\. _[_vfx\_mod\_fx\_instance\.cpp 95_](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/vfx/src/vfx_mod_fx_instance.cpp#L95)

In the `VFXModFXInstance::deserialize` function, there is an issue with the initialization of the `m_life.part_life_min` and `m_life.part_life_max` variables\. Here we can clearly see the impact of copy\-paste\. Both variables receive their values by calling the same function using the same `"lifeMin"`\. Logically, the minimum and maximum instance lifetimes should differ\. Therefore, we can assume that in the second case, developers should use`"lifeMin"` instead of `"lifeMax"`:

```cpp
m_life.part_life_min = blk->getReal("lifeMin", 5.0f);
m_life.part_life_max = blk->getReal("lifeMax", 5.0f);
```

**Fragment N3**

```cpp
Material* Material::clone() const
{
  auto material = new (std::nothrow) Material();
  if (material)
  {
    ....
    material->_textureSlots = material->_textureSlots;
    material->_textureSlotIndex = material->_textureSlotIndex;
    ....
  }
....
}
```

The PVS\-Studio warnings:

[_V570_](https://pvs-studio.com/en/docs/warnings/v570/)_ The 'material\-\>\_textureSlots' variable is assigned to itself\. _[_CCMaterial\.cpp 527_](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/cocos/renderer/CCMaterial.cpp#L527)

[_V570_](https://pvs-studio.com/en/docs/warnings/v570/)_ The 'material\-\>\_textureSlotIndex' variable is assigned to itself\. _[_CCMaterial\.cpp 528_](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/cocos/renderer/CCMaterial.cpp#L528)

Developers assign the `_textureSlots` and `_textureSlotIndex` variables to themselves, which makes no sense and may be an error\. Judging by the name of the class member function \(`clone`\), developers should have copied the object states into a new one\. 

The fix could be like this:

```cpp
material->_textureSlots = _textureSlots;
material->_textureSlotIndex = _textureSlotIndex;
```

### Logic flaws

**Fragment N4**

```cpp
template <typename T>
requires(std::is_enum_v<T>)
class TypedFlag
{
public:
  using EnumType = T;
  using ValueType = std::underlying_type_t<T>;
  ....
private:
  ValueType m_value = 0;
  ....
  friend TypedFlag<T> operator|(TypedFlag<T> value, TypedFlag<T> flags)
  {
  }
  ....
};
```

The PVS\-Studio warning: [_V591_](https://pvs-studio.com/en/docs/warnings/v591/)_ Non\-void function should return a value\. _[_typed\_flag\.h 145_](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/kernel/include/nau/utils/typed_flag.h#L145)

The `|` operator overload for the `TypedFlag` class template doesn't have a return value, which can cause errors in the program logic\. What makes this situation particularly interesting is the logic of the `[|=](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/kernel/include/nau/utils/typed_flag.h#L151-L154)` operator:

```cpp
friend TypedFlag<T>& operator|=(TypedFlag<T>& value, T flag)
{
  return value.set(flag);
}
```

There are several ways to fix this issue\.

**Option N1**\. Implement operator functionality\. They should return a result that aligns with the flag union logic\.

For example:

```cpp
return TypedFlag<T> { value }.set(flags);
```

**Option N2**\. Delete the operator\. If the `|` operator is not needed, we can delete it to avoid confusion\. Alternatively, we can declare the operator as deleted:

```cpp
friend TypedFlag<T> operator|(TypedFlag<T> value, TypedFlag<T> flags) = delete;
```

By implementing one of these fixes, the code becomes more predictable and easier to understand for other developers\.

And here are more instances where warnings have been issued:

* _V591 Non\-void function should return a value\. _[_typed\_flag\.h 149_](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/kernel/include/nau/utils/typed_flag.h#L149)
* _V591 Non\-void function should return a value\. _[_typed\_flag\.h 163_](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/kernel/include/nau/utils/typed_flag.h#L163)
* _V591 Non\-void function should return a value\. _[_string\.h 511_](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/kernel/include/nau/string/string.h#L511)

**Fragment N5**

```cpp
EntityId EntityManager::createEntitySync(....)
{
  ....
  if (EASTL_UNLIKELY(result != RequestResources::Loaded))
  {
#if NAU_DEBUG
    if (result == RequestResources::Loaded)
    {
      ....
    }
#endif
    if (result == RequestResources::Error)
    {
      ....
    }
  }
  ....
}
```

The PVS\-Studio warning: [_V637_](https://pvs-studio.com/en/docs/warnings/v637/)_ Two opposite conditions were encountered\. The second condition is always false\. Check lines: 711, 714\. _[_entityManager2\.cpp 711_](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/dagorECS/src/core/entityManager2.cpp#L711)

There is a logic error in the code that may be misleading\. Let's examine the details:

* The`if (EASTL_UNLIKELY(result != RequestResources::Loaded))` condition: it checks that the result is not equal to `RequestResources::Loaded`\. If this condition is true, the resources are not loaded\.
* The `#if NAU_DEBUG` block: the `if (result == RequestResources::Loaded)` check is always false, since we've already checked that `result` isn't equal to `RequestResources::Loaded`\. This creates a contradiction and can be misleading\.

**Fragment N6**

```cpp
IGenSave* create_async_writer(....)
{
  AsyncWriterCB* ret = new AsyncWriterCB(buf_size);
  if (!ret->open(fname, mode))
  {
    if (ret)
    {
      delete ret;
      ret = nullptr;
    }
  }
  return ret;
}
```

The PVS\-Studio warning: [_V668_](https://pvs-studio.com/en/docs/warnings/v668/)_ There is no sense in testing the 'ret' pointer against null, as the memory was allocated using the 'new' operator\. The exception will be generated in the case of memory allocation error\. [asyncWrite\.cpp 363](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/kernel/src/dag_ioSys/asyncWrite.cpp#L363)_

This code checks the `ret` pointer for `nullptr` after a call to `new`\. This can be a source of errors since the `ret` pointer will always point to the allocated memory if the allocation was successful\. If memory can't be allocated, an exception will be thrown and the code below won't be executed\.

In addition, the `ret` pointer is dereferenced before the check, which makes the `if (ret)` check less meaningful\. If `ret` was really equal to `nullptr`, the program would have already crashed while trying to dereference\.

Here are some other cases:

* _V668 There is no sense in testing the 'ret' pointer against null, as the memory was allocated using the 'new' operator\. The exception will be generated in the case of memory allocation error\. [asyncWrite\.cpp 377](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/kernel/src/dag_ioSys/asyncWrite.cpp#L377)_
* _V668 There is no sense in testing the 'tempFont' pointer against null, as the memory was allocated using the 'new' operator\. The exception will be generated in the case of memory allocation error\. [CCFontCharMap\.cpp 58](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/cocos/2d/CCFontCharMap.cpp#L58)_
* _V668 There is no sense in testing the 'tempFont' pointer against null, as the memory was allocated using the 'new' operator\. The exception will be generated in the case of memory allocation error\. [CCFontCharMap\.cpp 77](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/cocos/2d/CCFontCharMap.cpp#L77)_
* _V668 There is no sense in testing the 'tempFont' pointer against null, as the memory was allocated using the 'new' operator\. The exception will be generated in the case of memory allocation error\. [CCFontCharMap\.cpp 89](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/cocos/2d/CCFontCharMap.cpp#L89)_
* _V668 There is no sense in testing the 'tempFont' pointer against null, as the memory was allocated using the 'new' operator\. The exception will be generated in the case of memory allocation error\. [CCFontFNT\.cpp 523](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/cocos/2d/CCFontFNT.cpp#L523)_
* _V668 There is no sense in testing the 'tempFont' pointer against null, as the memory was allocated using the 'new' operator\. The exception will be generated in the case of memory allocation error\. [CCFontFNT\.cpp 545](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/cocos/2d/CCFontFNT.cpp#L545)_
* _V668 There is no sense in testing the 'tempFont' pointer against null, as the memory was allocated using the 'new' operator\. The exception will be generated in the case of memory allocation error\. [CCFontFNT\.cpp 569](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/cocos/2d/CCFontFNT.cpp#L569)_
* _V668 There is no sense in testing the 'layerGradient' pointer against null, as the memory was allocated using the 'new' operator\. The exception will be generated in the case of memory allocation error\. [CCLayer\.cpp 579](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/cocos/2d/CCLayer.cpp#L579)_
* _V668 There is no sense in testing the 'layerGradient' pointer against null, as the memory was allocated using the 'new' operator\. The exception will be generated in the case of memory allocation error\. [CCLayer\.cpp 592](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/cocos/2d/CCLayer.cpp#L592)_
* _V668 There is no sense in testing the 'pwszBuffer' pointer against null, as the memory was allocated using the 'new' operator\. The exception will be generated in the case of memory allocation error\. [CCDevice\-win32\.cpp 90](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/cocos/platform/win32/CCDevice-win32.cpp#L90)_
* _V668 There is no sense in testing the 'pwszBuffer' pointer against null, as the memory was allocated using the 'new' operator\. The exception will be generated in the case of memory allocation error\. [CCDevice\-win32\.cpp 326](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/cocos/platform/win32/CCDevice-win32.cpp#L326)_

**Fragment N7**

```cpp
void Properties::skipWhiteSpace()
{
  signed char c;
  do
  {
    c = readChar();
  } while (isspace(c) && c != EOF);
  ....
  if (c != EOF)
  {
    ....
  }
}
```

The PVS\-Studio warnings:

[_V739_](https://pvs-studio.com/en/docs/warnings/v739/)_ EOF should not be compared with a value of the 'char' type\. The 'c' should be of the 'int' type\. [CCProperties\.cpp 464](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/cocos/base/CCProperties.cpp#L464)_

[_V739_](https://pvs-studio.com/en/docs/warnings/v739/)_ EOF should not be compared with a value of the 'char' type\. The 'c' should be of the 'int' type\. [CCProperties\.cpp 468](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/cocos/base/CCProperties.cpp#L468)_

The `Properties::skipWhiteSpace` member function uses a variable of the `signed char` type to store the character read by `Properties::readChar`\. [Based on the comment](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/cocos/base/CCProperties.cpp#L411-L419), the last one simulates the behavior of [`std::getchar`](https://en.cppreference.com/w/cpp/io/c/getchar):

```cpp
//
// Stream simulation
//
signed char Properties::readChar()
{
  if (eof())
    return EOF;
  return _data->_bytes[(*_dataIdx)++];
}
```

This leads to two key issues:

**Error N1**\. The `std::getchar` function returns the `int` values for a reason—it returns a character \(1 byte\) or `EOF` to indicate an error\. `EOF` is a negative constant, usually `-1`\. Before converting the result of `std::getchar` to a character type, developers need to handle the case with `EOF`:

```cpp
if (int res = std::getchar(); res != EOF)
{
  char ch = res;
  // your logic with character
}
```

Why is it important? Developers who use [Extended ASCII Codes](https://en.wikipedia.org/wiki/Extended_ASCII) sometimes encounter an error when one of the alphabet characters is incorrectly handled by programs\.

The correct implementation of the `Properties::readChar` function should look like this:

```cpp
//
// Stream simulation
//
int Properties::readChar()
{
  if (eof())
    return EOF;
  return _data->_bytes[(*_dataIdx)++];
}
```

**Error N2**\. The `Properties::skipWhiteSpace` function after fixing `Properties::readChar` should also work with the `int` type, ensuring proper `EOF` handling:

```cpp
void Properties::skipWhiteSpace()
{
  int c;
  do
  {
    c = readChar();
  }
  while (c != EOF && isspace(c));
  ....
  if (c != EOF)
  {
    // Now we can cast 'c' to 'signed char'
    signed char ch = c;
    ....
  }
}
```

**Fragment N8**

```cpp
void Sweep::EdgeEvent(....)
{
  ....
  if (o1 == COLLINEAR) {
    if( triangle->Contains(&eq, p1)) {
    ....
    } else {
      std::runtime_error("EdgeEvent - collinear points not supported");
      assert(0);
    }
    return;
  }
  ....
  if (o2 == COLLINEAR) {
    if (triangle->Contains(&eq, p2)){
      ....
    } else {
      std::runtime_error("EdgeEvent - collinear points not supported");
      assert(0);
    }
    return;
  }
  ....
}
```

The PVS\-Studio warnings:

[_V596_](https://pvs-studio.com/en/docs/warnings/v596/)_ The object was created but it is not being used\. The 'throw' keyword could be missing: throw runtime\_error\(FOO\); [sweep\.cc 123](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/external/poly2tri/sweep/sweep.cc#L123)_

[_V596_](https://pvs-studio.com/en/docs/warnings/v596/)_ The object was created but it is not being used\. The 'throw' keyword could be missing: throw runtime\_error\(FOO\); [sweep\.cc 140](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/external/poly2tri/sweep/sweep.cc#L140)_

The `Sweep::EdgeEvent` member function creates `std::runtime_error` exception objects when collinear points are detected\. However, exceptions aren't thrown\. As a result, the program continues execution without handling the error, which can cause incorrect behavior\.

Here is the fixed code:

```cpp
throw std::runtime_error("EdgeEvent - collinear points not supported");
```

**Fragment N9**

```cpp
std::size_t UniformLocation::operator()(const UniformLocation &uniform) const
{
    return (((size_t) shaderStage) & 0xF)
           |((size_t)(location[0] << 4))
           |((size_t)(location[1] << 8));
}
```

The PVS\-Studio warnings:

[_V1028_](https://pvs-studio.com/en/docs/warnings/v1028/)_ Possible overflow\. Consider casting operands of the 'location\[0\] << 4' operator to the 'size\_t' type, not the result\. [Types\.cpp 40](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/cocos/renderer/backend/Types.cpp#L40)_

[_V1028_](https://pvs-studio.com/en/docs/warnings/v1028/)_ Possible overflow\. Consider casting operands of the 'location\[1\] << 8' operator to the 'size\_t' type, not the result\. [Types\.cpp 40](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/cocos/renderer/backend/Types.cpp#L40)_

The `UniformLocation::operator()` operator performs bitwise operations on values obtained from the `shaderStage` and `location` variables to return a unique identifier\.

The problem is that shift operations \(`<<`\) may cause an overflow if the values of `location[0]` or `location[1]` are too large\. To avoid this, developers could cast `location[0]` and `location[1]` to the `size_t` type before the shift operation:

```cpp
std::size_t UniformLocation::operator()(const UniformLocation &uniform) const
{
    return (((size_t) shaderStage) & 0xF)
           |((size_t)(location[0]) << 4)
           |((size_t)(location[1]) << 8);
}
```

### Memory issues

**Fragment N10**

```cpp
char** m_memBlock = nullptr;

template<class Type>
class ThreadLocalValue final
{
  ....
private:
  void resizeLines(size_t sizeReq)
  {
    ....
    auto newSize = sizeReq + 1;
    ....
    m_memBlock = static_cast<char**>(realloc(m_memBlock, 
                                             sizeof(char*) * newSize));
    ....
  }
  ....
};
```

The PVS\-Studio warning: [_V701_](https://pvs-studio.com/en/docs/warnings/v701/)_ realloc\(\) possible leak: when realloc\(\) fails in allocating memory, original pointer 'm\_memBlock' is lost\. Consider assigning realloc\(\) to a temporary pointer\. [thread\_local\_value\.h 235](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/kernel/include/nau/threading/thread_local_value.h#L235)_

In this code, `m_memBlock` is an array of `char` pointers, and the `resizeLines` function resizes it using `realloc`\.

If `realloc` successfully allocates memory, it returns either the same pointer or a new one having moved the data\. However, if the memory allocation fails, `realloc` returns `nullptr` and the previous value of `m_memBlock` is lost\.

To avoid memory leaks, developers should first assign the result of `realloc` to a temporary variable, check it for `nullptr`, and only then update `m_memBlock`\.

Here are some other cases:

* _V701 realloc\(\) possible leak: when realloc\(\) fails in allocating memory, original pointer '\_buffer' is lost\. Consider assigning realloc\(\) to a temporary pointer\. [CCDrawNode\.cpp 102](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/src/elements/draw_node.cpp#L102)_
* _V701 realloc\(\) possible leak: when realloc\(\) fails in allocating memory, original pointer '\_bufferGLPoint' is lost\. Consider assigning realloc\(\) to a temporary pointer\. [CCDrawNode\.cpp 116](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/src/elements/draw_node.cpp#L116)_
* _V701 realloc\(\) possible leak: when realloc\(\) fails in allocating memory, original pointer '\_bufferGLLine' is lost\. Consider assigning realloc\(\) to a temporary pointer\. [CCDrawNode\.cpp 130](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/src/elements/draw_node.cpp#L130)_
* _V701 realloc\(\) possible leak: when realloc\(\) fails in allocating memory, original pointer '\_triBatchesToDraw' is lost\. Consider assigning realloc\(\) to a temporary pointer\. [CCRenderer\.cpp 629](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/cocos/renderer/CCRenderer.cpp#L629)_
* _V701 realloc\(\) possible leak: when realloc\(\) fails in allocating memory, original pointer '\* out' is lost\. Consider assigning realloc\(\) to a temporary pointer\. [ZipUtils\.cpp 185](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/cocos/base/ZipUtils.cpp#L185)_
* _V701 realloc\(\) possible leak: when realloc\(\) fails in allocating memory, original pointer 'arr\-\>arr' is lost\. Consider assigning realloc\(\) to a temporary pointer\. [ccCArray\.cpp 100](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/cocos/base/ccCArray.cpp#L100)_

**Fragment N11**

```cpp
IAssetContainerLoader* loader = nullptr;
const auto importSettingsProviders = ....;
RuntimeReadonlyDictionary::Ptr importSettings;
for (const auto& importSettingsProvider : importSettingsProviders)
{
  if (importSettings = 
      importSettingsProvider->getAssetImportSettings(containerPath, 
                                                     *loader); 
      importSettings)
  {
    break;
  }
}
```

The PVS\-Studio warning: [_V522_](https://pvs-studio.com/en/docs/warnings/v522/)_ Dereferencing of the null pointer 'loader' might take place\. [asset\_file\_content\_provider\.cpp 34](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/assets/src/asset_file_content_provider.cpp#L34)_

At the beginning of the code, the `loader` pointer is initialized with `nullptr`\. Then, within a loop, the `getAssetImportSettings` member function is called, passing the dereferenced `loader` pointer as an argument\. Strangely, it's not modified in any way between these lines\. As a result, we catch guaranteed undefined behavior\.

Here's another case:

* _V522 Dereferencing of the null pointer 'object' might take place\. The null pointer is passed into 'replace' function\. Inspect the second argument\. Check lines: 'CCVector\.h:481', 'CCLayer\.cpp:976'\. [CCVector\.h 481](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/cocos/base/CCVector.h#L481)_

**Fragment N12**

```cpp
struct DelayedEntityCreationChunk
{
  ....
  DelayedEntityCreationChunk(uint16_t cap) : capacity(cap)
  {
    queue = (DelayedEntityCreation*)
      malloc(capacity * sizeof(DelayedEntityCreation));
  }
  ....
};
```

The PVS\-Studio warning: [_V630_](https://pvs-studio.com/en/docs/warnings/v630/)_ The 'malloc' function is used to allocate memory for an array of objects which are classes containing constructors and destructors\. [entityManager\.h 1391](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/dagorECS/include/daECS/core/entityManager.h#L1391)_

In this fragment, the analyzer warns that `malloc` is used to allocate memory for an array of class objects that have constructors and destructors\. Generally, it'd be better to use other ways of creating objects on the heap\. However, it's a false positive because object is created later via the [`emplace_back`](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/dagorECS/include/daECS/core/entityManager.h#L1440-L1446) function—roughly the same thing happens in the vector\.

The curious reader may question, "Why even mention this code fragment in the article?" We've planned to leave it out—until something else in this class caught our attention:

```cpp
struct DelayedEntityCreationChunk
{
  ....
  DelayedEntityCreation* queue = nullptr;
  uint16_t readFrom = 0, writeTo = 0, capacity;

  DelayedEntityCreationChunk(uint16_t cap) :
    capacity(cap)
  {
    queue = (DelayedEntityCreation*)
         malloc(capacity * sizeof(DelayedEntityCreation));
  }

  ~DelayedEntityCreationChunk()
  {
    for(auto i = begin(), e = end(); i != e; ++i)
      i->~DelayedEntityCreation();
    free(queue);
  }

  DelayedEntityCreationChunk(const DelayedEntityCreationChunk&) = delete;
  DelayedEntityCreationChunk&
    operator=(const DelayedEntityCreationChunk&) = delete;

  DelayedEntityCreationChunk(DelayedEntityCreationChunk&& a)
  {
    memcpy(this, &a, sizeof(DelayedEntityCreationChunk));
    memset(&a, 0, sizeof(DelayedEntityCreationChunk));
  }

  DelayedEntityCreationChunk& operator=(DelayedEntityCreationChunk&& a)
  {
    alignas(DelayedEntityCreationChunk) 
      char buf[sizeof(DelayedEntityCreationChunk)];

    memcpy(buf, this, sizeof(DelayedEntityCreationChunk));
    memcpy(this, &a, sizeof(DelayedEntityCreationChunk));
    memcpy(&a, buf, sizeof(DelayedEntityCreationChunk));
    return *this;
  }

  ....

  template <typename... Args>
  bool emplace_back(EntityId eid, Args &&...args)
  {
    DAECS_EXT_ASSERT(!full());
    new (queue + (writeTo++)) DelayedEntityCreation(eid,
                                             eastl::forward<Args>(args)...);
    return full();
  }
  ....
};
```

Here are class properties:

* [Non\-trivially destructible](https://en.cppreference.com/w/cpp/language/destructor#Trivial_destructor): a user\-defined destructor is defined\.
* [Non\-trivially relocatable](https://en.cppreference.com/w/cpp/language/move_constructor#Trivial_move_constructor): the user\-defined constructor and move operator are defined\. The move operators aren't marked as `noexcept`, which is [not good](https://en.cppreference.com/w/cpp/utility/move_if_noexcept) in some scenarios\.
* [Move\-only](https://en.cppreference.com/w/cpp/language/copy_constructor#Deleted_copy_constructor): the copy constructor and copy operator are deleted\.

Notice that the objects of this class—`memcpy` and `memset`—are actively used in the constructor and move operator\. The standard clearly regulates their behavior only for [trivially copyable](https://en.cppreference.com/w/cpp/language/classes#Trivially_copyable_class) types, otherwise it [may be undefined](https://stackoverflow.com/questions/29777492)\.

Developers could fix code like this:

```cpp
struct DelayedEntityCreationChunk
{
  ....
  DelayedEntityCreation* queue = nullptr;
  uint16_t readFrom = 0, writeTo = 0, capacity;
  ....
public:
  DelayedEntityCreationChunk(DelayedEntityCreationChunk &&a) noexcept
    : queue { std::exchange(a.queue, {}) }
    , readFrom { std::exchange(a.readFrom, {}) }
    , writeTo { std::exchange(a.writeTo, {}) }
    , capacity { std::exchange(a.capacity, {}) }
  {
  }

  void swap(DelayedEntityCreationChunk &other) noexcept
  {
    auto lhs = std::tie(queue, readFrom, writeTo, capacity);
    auto rhs = std::tie(other.queue, other.readFrom,
                        other.writeTo, other.capacity);

    std::swap(lhs, rhs);
  }

  DelayedEntityCreationChunk&
    operator=(DelayedEntityCreationChunk &&a) noexcept
  {
    if (this == std::addressof(a)) return *this;

    auto tmp = std::move(a);
    swap(tmp);
    return *this;
  }
  ....
};
```

## Wrap\-up

In the first part of our deep dive into Nau Engine codebase, we've analyzed its key features, focusing on copy\-paste errors, logic flaws, and memory issues\. These issues can become serious roadblocks for developers aiming to create engaging games\. While Nau Engine offers many promising features, the detected flaws could significantly impact performance and the overall gaming experience\.

In the next part of the trilogy, we'll be looking for ways to optimize and enhance the engine's performance\.

Stay tuned, and thanks for reading\!