Case 3 has several subcases based on how you distinguish an empty buffer from a full one. For brevity, I will use N to refer to the capacity.
3a) Already mentioned: allocate N + 1 slots and report full if you are about to write into the last one.
3b) Keep the read and write indices mod N and use a boolean to distinguish full from empty.
3c) Increment both indices forever, but reduce them mod N when you use them. Eventually, they will overflow, but this may not be practical concern.
3d) Reduce the indices mod 2N when you increment them, and mod N when you use them. This eliminates the possibility of overflow but leaves enough index space so that full and empty look different.