docs: @Nullable
      private ByteBuf checkForCompletion(GELFMessage gelfMessage) {
          if (!chunks.isEmpty() && log.isDebugEnabled()) {
              log.debug("Dumping GELF chunk map [chunks for {} messages]:\n{}", chunks.size(), humanReadableChunkMap());
          }
          final GELFMessageChunk chunk = new GELFMessageChunk(gelfMessage, null); // TODO second parameter
          final int sequenceCount = chunk.getSequenceCount();
  
          final String messageId = chunk.getId();
  
          ChunkEntry entry = new ChunkEntry(sequenceCount, chunk.getArrival(), messageId);
  
          final ChunkEntry existing = chunks.putIfAbsent(messageId, entry);
          if (existing == null) {
              // add this chunk entry to the eviction set
              waitingMessages.inc();
              sortedEvictionSet.add(entry);
          } else {
              // the entry is already in the eviction set and chunk map
              entry = existing;
          }
  
          final int sequenceNumber = chunk.getSequenceNumber();
          if (!entry.payloadArray.compareAndSet(sequenceNumber, null, chunk)) {
              log.error("Received duplicate chunk {} for message {} from {}", sequenceNumber, messageId, gelfMessage.getSourceAddress());
              duplicateChunks.inc();
              return null;
          }
  
          final int chunkWatermark = entry.chunkSlotsWritten.incrementAndGet();
  
          if (chunkWatermark > MAX_CHUNKS) {
              getAndCleanupEntry(messageId);
              throw new IllegalStateException("Maximum number of chunks reached, discarding message");
          }
  
          if (chunkWatermark == sequenceCount) {
              // message is complete by chunk count, assemble and return it.
              // it might still be corrupt etc, but we've seen enough chunks
              // remove before operating on it, to avoid racing too much with the clean up job, some race is inevitable, though.
              entry = getAndCleanupEntry(messageId);
  
              final byte[] allChunks[] = new byte[sequenceCount][];
              for (int i = 0; i < entry.payloadArray.length(); i++) {
                  final GELFMessageChunk messageChunk = entry.payloadArray.get(i);
                  if (messageChunk == null) {
                      log.debug("Couldn't read chunk {} of message {}, skipping this chunk.", i, messageId);
                  } else {
                      allChunks[i] = messageChunk.getData();
                  }
              }
              completeMessages.inc();
              return Unpooled.wrappedBuffer(allChunks);
          }
  
          // message isn't complete yet, check if we should remove the other parts as well
          if (isOutdated(entry)) {
              // chunks are outdated, the oldest came in over 5 seconds ago, clean them all up
              log.debug("Not all chunks of <{}> arrived within {}ms. Dropping chunks.", messageId, VALIDITY_PERIOD);
              expireEntry(messageId);
          }
  
          return null;
      }