Faf du Plessis agonisingly fell one run short of a maiden Test double hundred as South Africa took control of the first Test against Sri Lanka on day three in Centurion.

In pursuit of Sri Lanka's first innings 396, the Proteas compiled a formidable 621 all out and the tourists were 65-2 at the close in their second innings, 160 runs behind.

Du Plessis' knock was his highest score in Test cricket, eclipsing the 137 he made against New Zealand in January and also beat his first-class best of 176.

The 36-year-old was eventually dismissed on 199 by the leg spin of Wanindu Hasaranga as he attempted to hit the ball over mid-on only to pick out Dimuth Karunaratne.

South Africa began the day on 317-4 and Sri Lanka's bowling attack was dealt an early blow in the sixth over when opening bowler Kasun Rajitha left the field clutching his groin.

Lahiru Kumara subsequently departed with a groin injury of his own, while play was briefly interrupted when wicketkeeper Niroshan Dickwella was stung by a bee.

Temba Bavuma brought up his half century before a curious dismissal on 71 at 399-5 as he walked having slashed at a wide delivery from Dasun Shanaka (2-98).

Dickwella took the catch but television replays showed there was no contact between Bavuma's bat and the ball.

Du Plessis brought up his 10th Test hundred, off 150 balls, just before lunch after he worked a ball from Hasaranga through point off the back foot for four.

Wiaan Mulder (36) added 77 for the sixth wicket with Du Plessis before he edged into Dickwella's gloves as he tried to cut a delivery from Hasaranga.

Du Plessis oozed class – striking Shanaka for two fours in an over – as he moved to 150 with South Africa's middle order confidently taking on Sri Lanka's depleted attack.

Keshav Maharaj began to go through gears, punishing a Kusal Mendis full toss with an effortless six over square leg.

Du Plessis survived a close stumping appeal on 191 when a ball from Hasaranga spun sharply and Dickwella whipped off the bails, but the third umpire deemed his foot was grounded.

With Maharaj playing fluently Du Plessis seemed content to reach his double century in singles, but one run short of the milestone he tamely spooned Hasaranga (4-171) into the hands of Karunaratne.

Du Plessis rocked his head back in disappointment as he became the 11th player in the history of Test cricket to be dismissed while on 199.

Maharaj continued to be the aggressor as South Africa's tail failed to wag before he was last man out when he skied Vishwa Fernando (3-129) for a Test best 73.

Sri Lanka were left with 12 overs to negotiate at the end of the day but Lungi Ngidi (2-28) did the damage before the close, removing captain Karunaratne's off stump while Kusal nicked off.

Dinesh Chandimal and Kusal Perera led the fightback with some attacking strokeplay and the pair will resume on 21 and 33 respectively on day four.